Core/Auras: removed caster dependency from core
Port From (https://github.com/TrinityCore/TrinityCore/commit/fccf1a8c62820004df92b9ac567ba75f7281e6e2)
This commit is contained in:
@@ -568,7 +568,7 @@ namespace Game.Chat
|
||||
// flat melee damage without resistence/etc reduction
|
||||
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)
|
||||
attacker.SendAttackStateUpdate(HitInfo.AffectsVictim, target, SpellSchoolMask.Normal, damage_, 0, 0, VictimState.Hit, 0);
|
||||
return true;
|
||||
@@ -579,8 +579,8 @@ namespace Game.Chat
|
||||
|
||||
SpellSchoolMask schoolmask = (SpellSchoolMask)(1 << school);
|
||||
|
||||
if (attacker.IsDamageReducedByArmor(schoolmask))
|
||||
damage_ = attacker.CalcArmorReducedDamage(handler.GetPlayer(), target, damage_, null, WeaponAttackType.BaseAttack);
|
||||
if (Unit.IsDamageReducedByArmor(schoolmask))
|
||||
damage_ = Unit.CalcArmorReducedDamage(handler.GetPlayer(), target, damage_, null, WeaponAttackType.BaseAttack);
|
||||
|
||||
string spellStr = args.NextString();
|
||||
|
||||
@@ -588,7 +588,7 @@ namespace Game.Chat
|
||||
if (string.IsNullOrEmpty(spellStr))
|
||||
{
|
||||
DamageInfo dmgInfo = new(attacker, target, damage_, null, schoolmask, DamageEffectType.SpellDirect, WeaponAttackType.BaseAttack);
|
||||
attacker.CalcAbsorbResist(dmgInfo);
|
||||
Unit.CalcAbsorbResist(dmgInfo);
|
||||
|
||||
if (dmgInfo.GetDamage() == 0)
|
||||
return true;
|
||||
@@ -597,8 +597,8 @@ namespace Game.Chat
|
||||
|
||||
uint absorb = dmgInfo.GetAbsorb();
|
||||
uint resist = dmgInfo.GetResist();
|
||||
attacker.DealDamageMods(target, ref damage_, ref absorb);
|
||||
attacker.DealDamage(target, damage_, null, DamageEffectType.Direct, schoolmask, null, false);
|
||||
Unit.DealDamageMods(attacker, target, ref damage_, ref absorb);
|
||||
Unit.DealDamage(attacker, target, damage_, null, DamageEffectType.Direct, schoolmask, null, false);
|
||||
attacker.SendAttackStateUpdate(HitInfo.AffectsVictim, target, schoolmask, damage_, absorb, resist, VictimState.Hit, 0);
|
||||
return true;
|
||||
}
|
||||
@@ -613,9 +613,9 @@ namespace Game.Chat
|
||||
if (spellInfo == null)
|
||||
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_;
|
||||
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.SendSpellNonMeleeDamageLog(damageInfo);
|
||||
return true;
|
||||
@@ -670,7 +670,7 @@ namespace Game.Chat
|
||||
return false;
|
||||
|
||||
if (target.IsAlive())
|
||||
handler.GetSession().GetPlayer().Kill(target);
|
||||
Unit.Kill(handler.GetSession().GetPlayer(), target);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3956,7 +3956,7 @@ namespace Game.Entities
|
||||
break;
|
||||
}
|
||||
|
||||
DealDamageMods(this, ref damage, ref absorb);
|
||||
DealDamageMods(null, this, ref damage, ref absorb);
|
||||
|
||||
EnvironmentalDamageLog packet = new();
|
||||
packet.Victim = GetGUID();
|
||||
@@ -3965,7 +3965,7 @@ namespace Game.Entities
|
||||
packet.Absorbed = (int)absorb;
|
||||
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);
|
||||
|
||||
SendCombatLogMessage(packet);
|
||||
|
||||
@@ -667,7 +667,7 @@ namespace Game.Entities
|
||||
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)
|
||||
{
|
||||
@@ -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
|
||||
if (IsVehicle() || (victim.IsVehicle() && !victim.IsTypeId(TypeId.Player)))
|
||||
return;
|
||||
|
||||
// Don't consider resilience if not in PvP - player or pet
|
||||
if (!GetCharmerOrOwnerPlayerOrPlayerItself())
|
||||
if (victim.IsVehicle() && !victim.IsPlayer())
|
||||
return;
|
||||
|
||||
Unit target = null;
|
||||
if (victim.IsTypeId(TypeId.Player))
|
||||
if (victim.IsPlayer())
|
||||
target = victim;
|
||||
else if (victim.IsTypeId(TypeId.Unit) && victim.GetOwner() && victim.GetOwner().IsTypeId(TypeId.Player))
|
||||
target = victim.GetOwner();
|
||||
else // victim->GetTypeId() == TYPEID_UNIT
|
||||
{
|
||||
Unit owner = victim.GetOwner();
|
||||
if (owner != null)
|
||||
if (owner.IsPlayer())
|
||||
target = owner;
|
||||
}
|
||||
|
||||
if (!target)
|
||||
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%)
|
||||
uint GetDamageReduction(uint damage) { return GetCombatRatingDamageReduction(CombatRating.ResiliencePlayerDamage, 1.0f, 100.0f, damage); }
|
||||
|
||||
@@ -802,7 +818,7 @@ namespace Game.Entities
|
||||
return missChance;
|
||||
}
|
||||
|
||||
float GetUnitCriticalChance(WeaponAttackType attackType, Unit victim)
|
||||
float GetUnitCriticalChanceDone(WeaponAttackType attackType)
|
||||
{
|
||||
float chance = 0.0f;
|
||||
Player thisPlayer = ToPlayer();
|
||||
@@ -830,25 +846,38 @@ namespace Game.Entities
|
||||
chance += GetTotalAuraModifier(AuraType.ModCritPct);
|
||||
}
|
||||
}
|
||||
return chance;
|
||||
}
|
||||
|
||||
float GetUnitCriticalChanceTaken(Unit attacker, WeaponAttackType attackType, float critDone)
|
||||
{
|
||||
float chance = critDone;
|
||||
|
||||
// flat aura mods
|
||||
if (attackType == WeaponAttackType.RangedAttack)
|
||||
chance += victim.GetTotalAuraModifier(AuraType.ModAttackerRangedCritChance);
|
||||
chance += GetTotalAuraModifier(AuraType.ModAttackerRangedCritChance);
|
||||
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();
|
||||
if (tempSummon)
|
||||
chance += victim.GetTotalAuraModifier(AuraType.ModCritChanceForCasterPet, aurEff => aurEff.GetCasterGUID() == tempSummon.GetSummonerGUID());
|
||||
TempSummon tempSummon = attacker.ToTempSummon();
|
||||
if (tempSummon != null)
|
||||
chance += GetTotalAuraModifier(AuraType.ModCritChanceForCasterPet, aurEff => aurEff.GetCasterGUID() == tempSummon.GetSummonerGUID());
|
||||
|
||||
chance += victim.GetTotalAuraModifier(AuraType.ModAttackerSpellAndWeaponCritChance);
|
||||
chance += GetTotalAuraModifier(AuraType.ModAttackerSpellAndWeaponCritChance);
|
||||
|
||||
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)
|
||||
{
|
||||
int levelDiff = (int)(victim.GetLevelForTarget(this) - GetLevelForTarget(victim));
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace Game.Entities
|
||||
InterruptNonMeleeSpells(false);
|
||||
|
||||
RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.EnteringCombat);
|
||||
ProcSkillsAndAuras(null, ProcFlags.EnterCombat, ProcFlags.None, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null);
|
||||
ProcSkillsAndAuras(this, null, ProcFlags.EnterCombat, ProcFlags.None, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null);
|
||||
}
|
||||
|
||||
public virtual void AtExitCombat()
|
||||
@@ -558,13 +558,13 @@ namespace Game.Entities
|
||||
CalcDamageInfo damageInfo;
|
||||
CalculateMeleeDamage(victim, 0, out damageInfo, attType);
|
||||
// Send log damage message to client
|
||||
DealDamageMods(victim, ref damageInfo.damage, ref damageInfo.absorb);
|
||||
DealDamageMods(damageInfo.attacker, victim, ref damageInfo.damage, ref damageInfo.absorb);
|
||||
SendAttackStateUpdate(damageInfo);
|
||||
|
||||
DealMeleeDamage(damageInfo, true);
|
||||
|
||||
DamageInfo dmgInfo = new(damageInfo);
|
||||
ProcSkillsAndAuras(damageInfo.target, damageInfo.procAttacker, damageInfo.procVictim, ProcFlagsSpellType.None, ProcFlagsSpellPhase.None, dmgInfo.GetHitMask(), null, dmgInfo, null);
|
||||
ProcSkillsAndAuras(damageInfo.attacker, damageInfo.target, damageInfo.procAttacker, damageInfo.procVictim, ProcFlagsSpellType.None, ProcFlagsSpellPhase.None, dmgInfo.GetHitMask(), null, dmgInfo, null);
|
||||
Log.outDebug(LogFilter.Unit, "AttackerStateUpdate: {0} attacked {1} for {2} dmg, absorbed {3}, blocked {4}, resisted {5}.",
|
||||
GetGUID().ToString(), victim.GetGUID().ToString(), damageInfo.damage, damageInfo.absorb, damageInfo.blocked_amount, damageInfo.resist);
|
||||
}
|
||||
@@ -756,71 +756,6 @@ namespace Game.Entities
|
||||
}
|
||||
}
|
||||
|
||||
public void SetInCombatState(bool PvP, Unit enemy = null)
|
||||
{
|
||||
// only alive units can be in combat
|
||||
if (!IsAlive())
|
||||
return;
|
||||
|
||||
if (PvP)
|
||||
{
|
||||
combatTimer = 5000;
|
||||
Player me = ToPlayer();
|
||||
if (me)
|
||||
me.EnablePvpRules(true);
|
||||
}
|
||||
|
||||
if (IsInCombat() || HasUnitState(UnitState.Evade))
|
||||
return;
|
||||
|
||||
AddUnitFlag(UnitFlags.InCombat);
|
||||
|
||||
Creature creature = ToCreature();
|
||||
if (creature != null)
|
||||
{
|
||||
// Set home position at place of engaging combat for escorted creatures
|
||||
if ((IsAIEnabled && creature.GetAI().IsEscorted()) || GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Waypoint ||
|
||||
GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Point)
|
||||
creature.SetHomePosition(GetPositionX(), GetPositionY(), GetPositionZ(), Orientation);
|
||||
|
||||
if (enemy != null)
|
||||
{
|
||||
if (IsAIEnabled)
|
||||
creature.GetAI().JustEngagedWith(enemy);
|
||||
|
||||
if (creature.GetFormation() != null)
|
||||
creature.GetFormation().MemberEngagingTarget(creature, enemy);
|
||||
}
|
||||
|
||||
if (IsPet())
|
||||
{
|
||||
UpdateSpeed(UnitMoveType.Run);
|
||||
UpdateSpeed(UnitMoveType.Swim);
|
||||
UpdateSpeed(UnitMoveType.Flight);
|
||||
}
|
||||
|
||||
if (!creature.GetCreatureTemplate().TypeFlags.HasAnyFlag(CreatureTypeFlags.MountedCombatAllowed))
|
||||
Dismount();
|
||||
}
|
||||
|
||||
foreach (var unit in m_Controlled)
|
||||
unit.SetInCombatState(PvP, enemy);
|
||||
|
||||
foreach (var pair in GetAppliedAuras())
|
||||
{
|
||||
AuraApplication aurApp = pair.Value;
|
||||
aurApp.GetBase().CallScriptEnterLeaveCombatHandlers(aurApp, true);
|
||||
}
|
||||
|
||||
Spell spell = GetCurrentSpell(CurrentSpellTypes.Generic);
|
||||
if (spell != null)
|
||||
if (spell.GetState() == SpellState.Preparing && spell.m_spellInfo.HasAttribute(SpellAttr0.CantUsedInCombat) && spell.m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.Combat))
|
||||
InterruptNonMeleeSpells(false);
|
||||
|
||||
RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.EnteringCombat);
|
||||
ProcSkillsAndAuras(enemy, ProcFlags.EnterCombat, ProcFlags.None, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null);
|
||||
}
|
||||
|
||||
internal void SendCombatLogMessage(CombatLogServerPacket combatLog)
|
||||
{
|
||||
CombatLogSender combatLogSender = new(combatLog);
|
||||
@@ -837,14 +772,20 @@ namespace Game.Entities
|
||||
return !m_threatManager.IsThreatListEmpty();
|
||||
}
|
||||
|
||||
public void Kill(Unit victim, bool durabilityLoss = true, bool skipSettingDeathState = false)
|
||||
public static void Kill(Unit attacker, Unit victim, bool durabilityLoss = true, bool skipSettingDeathState = false)
|
||||
{
|
||||
// Prevent killing unit twice (and giving reward from kill twice)
|
||||
if (victim.GetHealth() == 0)
|
||||
return;
|
||||
|
||||
if (attacker != null && !attacker.IsInMap(victim))
|
||||
attacker = null;
|
||||
|
||||
// find player: owner of controlled `this` or `this` itself maybe
|
||||
Player player = GetCharmerOrOwnerPlayerOrPlayerItself();
|
||||
Player player = null;
|
||||
if (attacker != null)
|
||||
player = attacker.GetCharmerOrOwnerPlayerOrPlayerItself();
|
||||
|
||||
Creature creature = victim.ToCreature();
|
||||
|
||||
bool isRewardAllowed = true;
|
||||
@@ -911,7 +852,7 @@ namespace Game.Entities
|
||||
loot.Clear();
|
||||
uint lootid = creature.GetCreatureTemplate().LootId;
|
||||
if (lootid != 0)
|
||||
loot.FillLoot(lootid, LootStorage.Creature, looter, false, false, creature.GetLootMode(), GetMap().GetDifficultyLootItemContext());
|
||||
loot.FillLoot(lootid, LootStorage.Creature, looter, false, false, creature.GetLootMode(), creature.GetMap().GetDifficultyLootItemContext());
|
||||
|
||||
if (creature.GetLootMode() > 0)
|
||||
loot.GenerateMoneyLoot(creature.GetCreatureTemplate().MinGold, creature.GetCreatureTemplate().MaxGold);
|
||||
@@ -933,24 +874,28 @@ namespace Game.Entities
|
||||
}
|
||||
|
||||
// Do KILL and KILLED procs. KILL proc is called only for the unit who landed the killing blow (and its owner - for pets and totems) regardless of who tapped the victim
|
||||
if (IsPet() || IsTotem())
|
||||
if (attacker != null && (attacker.IsPet() || attacker.IsTotem()))
|
||||
{
|
||||
// proc only once for victim
|
||||
Unit owner = GetOwner();
|
||||
Unit owner = attacker.GetOwner();
|
||||
if (owner != null)
|
||||
owner.ProcSkillsAndAuras(victim, ProcFlags.Kill, ProcFlags.None, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null);
|
||||
ProcSkillsAndAuras(owner, victim, ProcFlags.Kill, ProcFlags.None, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null);
|
||||
}
|
||||
|
||||
if (!victim.IsCritter())
|
||||
ProcSkillsAndAuras(victim, ProcFlags.Kill, ProcFlags.Killed, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null);
|
||||
ProcSkillsAndAuras(attacker, victim, ProcFlags.Kill, ProcFlags.Killed, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null);
|
||||
|
||||
// Proc auras on death - must be before aura/combat remove
|
||||
victim.ProcSkillsAndAuras(victim, ProcFlags.None, ProcFlags.Death, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null);
|
||||
ProcSkillsAndAuras(victim, victim, ProcFlags.None, ProcFlags.Death, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null);
|
||||
|
||||
// update get killing blow achievements, must be done before setDeathState to be able to require auras on target
|
||||
// and before Spirit of Redemption as it also removes auras
|
||||
if (player != null)
|
||||
player.UpdateCriteria(CriteriaTypes.GetKillingBlows, 1, 0, 0, victim);
|
||||
if (attacker != null)
|
||||
{
|
||||
Player killerPlayer = attacker.GetCharmerOrOwnerPlayerOrPlayerItself();
|
||||
if (killerPlayer != null)
|
||||
killerPlayer.UpdateCriteria(CriteriaTypes.GetKillingBlows, 1, 0, 0, victim);
|
||||
}
|
||||
|
||||
if (!skipSettingDeathState)
|
||||
{
|
||||
@@ -985,11 +930,11 @@ namespace Game.Entities
|
||||
// Durability loss is calculated more accurately again for each item in Player.DurabilityLoss
|
||||
plrVictim.DurabilityLossAll(baseLoss, false);
|
||||
// durability lost message
|
||||
SendDurabilityLoss(plrVictim, loss);
|
||||
plrVictim.SendDurabilityLoss(plrVictim, loss);
|
||||
}
|
||||
// Call KilledUnit for creatures
|
||||
if (IsTypeId(TypeId.Unit) && IsAIEnabled)
|
||||
ToCreature().GetAI().KilledUnit(victim);
|
||||
if (attacker != null && attacker.IsCreature() && attacker.IsAIEnabled)
|
||||
attacker.ToCreature().GetAI().KilledUnit(victim);
|
||||
|
||||
// last damage from non duel opponent or opponent controlled creature
|
||||
if (plrVictim.duel != null)
|
||||
@@ -1015,12 +960,12 @@ namespace Game.Entities
|
||||
}
|
||||
|
||||
// Call KilledUnit for creatures, this needs to be called after the lootable flag is set
|
||||
if (IsTypeId(TypeId.Unit) && IsAIEnabled)
|
||||
ToCreature().GetAI().KilledUnit(victim);
|
||||
if (attacker != null && attacker.IsCreature() && attacker.IsAIEnabled)
|
||||
attacker.ToCreature().GetAI().KilledUnit(victim);
|
||||
|
||||
// Call creature just died function
|
||||
if (creature.IsAIEnabled)
|
||||
creature.GetAI().JustDied(this);
|
||||
creature.GetAI().JustDied(attacker);
|
||||
|
||||
TempSummon summon = creature.ToTempSummon();
|
||||
if (summon != null)
|
||||
@@ -1028,22 +973,21 @@ namespace Game.Entities
|
||||
Unit summoner = summon.GetSummoner();
|
||||
if (summoner != null)
|
||||
if (summoner.IsTypeId(TypeId.Unit) && summoner.IsAIEnabled)
|
||||
summoner.ToCreature().GetAI().SummonedCreatureDies(creature, this);
|
||||
summoner.ToCreature().GetAI().SummonedCreatureDies(creature, attacker);
|
||||
}
|
||||
|
||||
// Dungeon specific stuff, only applies to players killing creatures
|
||||
if (creature.GetInstanceId() != 0)
|
||||
{
|
||||
Map instanceMap = creature.GetMap();
|
||||
Player creditedPlayer = GetCharmerOrOwnerPlayerOrPlayerItself();
|
||||
// @todo do instance binding anyway if the charmer/owner is offline
|
||||
|
||||
if (instanceMap.IsDungeon() && (creditedPlayer || this == victim))
|
||||
/// @todo do instance binding anyway if the charmer/owner is offline
|
||||
if (instanceMap.IsDungeon() && ((attacker != null && attacker.GetCharmerOrOwnerPlayerOrPlayerItself() != null) || attacker == victim))
|
||||
{
|
||||
if (instanceMap.IsRaidOrHeroicDungeon())
|
||||
{
|
||||
if (creature.GetCreatureTemplate().FlagsExtra.HasAnyFlag(CreatureFlagsExtra.InstanceBind))
|
||||
((InstanceMap)instanceMap).PermBindAllPlayers();
|
||||
instanceMap.ToInstanceMap().PermBindAllPlayers();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1061,7 +1005,7 @@ namespace Game.Entities
|
||||
|
||||
// outdoor pvp things, do these after setting the death state, else the player activity notify won't work... doh...
|
||||
// handle player kill only if not suicide (spirit of redemption for example)
|
||||
if (player != null && this != victim)
|
||||
if (player != null && attacker != victim)
|
||||
{
|
||||
OutdoorPvP pvp = player.GetOutdoorPvP();
|
||||
if (pvp != null)
|
||||
@@ -1087,35 +1031,44 @@ namespace Game.Entities
|
||||
}
|
||||
|
||||
// achievement stuff
|
||||
if (victim.IsTypeId(TypeId.Player))
|
||||
if (attacker != null && victim.IsPlayer())
|
||||
{
|
||||
if (IsTypeId(TypeId.Unit))
|
||||
victim.ToPlayer().UpdateCriteria(CriteriaTypes.KilledByCreature, GetEntry());
|
||||
else if (IsTypeId(TypeId.Player) && victim != this)
|
||||
victim.ToPlayer().UpdateCriteria(CriteriaTypes.KilledByPlayer, 1, (ulong)ToPlayer().GetTeam());
|
||||
if (attacker.IsCreature())
|
||||
victim.ToPlayer().UpdateCriteria(CriteriaTypes.KilledByCreature, attacker.GetEntry());
|
||||
else if (attacker.IsPlayer() && victim != attacker)
|
||||
victim.ToPlayer().UpdateCriteria(CriteriaTypes.KilledByPlayer, 1, (ulong)attacker.ToPlayer().GetTeam());
|
||||
}
|
||||
|
||||
// Hook for OnPVPKill Event
|
||||
Player killerPlr = ToPlayer();
|
||||
Creature killerCre = ToCreature();
|
||||
if (killerPlr != null)
|
||||
if (attacker != null)
|
||||
{
|
||||
Player killedPlr = victim.ToPlayer();
|
||||
Creature killedCre = victim.ToCreature();
|
||||
if (killedPlr != null)
|
||||
Global.ScriptMgr.OnPVPKill(killerPlr, killedPlr);
|
||||
else if (killedCre != null)
|
||||
Global.ScriptMgr.OnCreatureKill(killerPlr, killedCre);
|
||||
}
|
||||
else if (killerCre != null)
|
||||
{
|
||||
Player killed = victim.ToPlayer();
|
||||
if (killed != null)
|
||||
Global.ScriptMgr.OnPlayerKilledByCreature(killerCre, killed);
|
||||
Player killerPlr = attacker.ToPlayer();
|
||||
if (killerPlr != null)
|
||||
{
|
||||
Player killedPlr = victim.ToPlayer();
|
||||
if (killedPlr != null)
|
||||
Global.ScriptMgr.OnPVPKill(killerPlr, killedPlr);
|
||||
else
|
||||
{
|
||||
Creature killedCre = victim.ToCreature();
|
||||
if (killedCre != null)
|
||||
Global.ScriptMgr.OnCreatureKill(killerPlr, killedCre);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Creature killerCre = attacker.ToCreature();
|
||||
if (killerCre != null)
|
||||
{
|
||||
Player killed = victim.ToPlayer();
|
||||
if (killed != null)
|
||||
Global.ScriptMgr.OnPlayerKilledByCreature(killerCre, killed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void KillSelf(bool durabilityLoss = true, bool skipSettingDeathState = false) { Kill(this, durabilityLoss, skipSettingDeathState); }
|
||||
public void KillSelf(bool durabilityLoss = true, bool skipSettingDeathState = false) { Kill(this, this, durabilityLoss, skipSettingDeathState); }
|
||||
|
||||
public virtual bool CanUseAttackType(WeaponAttackType attacktype)
|
||||
{
|
||||
@@ -1289,11 +1242,13 @@ namespace Game.Entities
|
||||
if (!damageInfo.HitInfo.HasAnyFlag(HitInfo.Miss))
|
||||
damageInfo.HitInfo |= HitInfo.AffectsVictim;
|
||||
|
||||
uint resilienceReduction = damageInfo.damage;
|
||||
ApplyResilience(victim, ref resilienceReduction);
|
||||
resilienceReduction = damageInfo.damage - resilienceReduction;
|
||||
damageInfo.damage -= resilienceReduction;
|
||||
damageInfo.cleanDamage += resilienceReduction;
|
||||
int resilienceReduction = (int)damageInfo.damage;
|
||||
if (CanApplyResilience())
|
||||
ApplyResilience(victim, ref resilienceReduction);
|
||||
|
||||
resilienceReduction = (int)damageInfo.damage - resilienceReduction;
|
||||
damageInfo.damage -= (uint)resilienceReduction;
|
||||
damageInfo.cleanDamage += (uint)resilienceReduction;
|
||||
|
||||
// Calculate absorb resist
|
||||
if (damageInfo.damage > 0)
|
||||
@@ -1326,7 +1281,7 @@ namespace Game.Entities
|
||||
int miss_chance = (int)(MeleeSpellMissChance(victim, attType, null) * 100.0f);
|
||||
|
||||
// Critical hit chance
|
||||
int crit_chance = (int)(GetUnitCriticalChance(attType, victim) + GetTotalAuraModifier(AuraType.ModAutoAttackCritChance) * 100.0f);
|
||||
int crit_chance = (int)((GetUnitCriticalChanceAgainst(attType, victim) + GetTotalAuraModifier(AuraType.ModAutoAttackCritChance)) * 100.0f);
|
||||
|
||||
int dodge_chance = (int)(GetUnitDodgeChance(attType, victim) * 100.0f);
|
||||
int block_chance = (int)(GetUnitBlockChance(attType, victim) * 100.0f);
|
||||
|
||||
@@ -105,6 +105,7 @@ namespace Game.Entities
|
||||
}
|
||||
|
||||
int DoneTotal = 0;
|
||||
float DoneTotalMod = SpellDamagePctDone(victim, spellProto, damagetype);
|
||||
|
||||
// Done fixed damage bonus auras
|
||||
int DoneAdvertisedBenefit = SpellBaseDamageBonusDone(spellProto.GetSchoolMask());
|
||||
@@ -141,27 +142,21 @@ namespace Game.Entities
|
||||
float coeff = effect.BonusCoefficient;
|
||||
if (DoneAdvertisedBenefit != 0)
|
||||
{
|
||||
Player modOwner = GetSpellModOwner();
|
||||
if (modOwner)
|
||||
Player modOwner1 = GetSpellModOwner();
|
||||
if (modOwner1)
|
||||
{
|
||||
coeff *= 100.0f;
|
||||
modOwner.ApplySpellMod(spellProto, SpellModOp.BonusCoefficient, ref coeff);
|
||||
modOwner1.ApplySpellMod(spellProto, SpellModOp.BonusCoefficient, ref coeff);
|
||||
coeff /= 100.0f;
|
||||
}
|
||||
DoneTotal += (int)(DoneAdvertisedBenefit * coeff * stack);
|
||||
}
|
||||
|
||||
// Done Percentage for DOT is already calculated, no need to do it again. The percentage mod is applied in Aura.HandleAuraSpecificMods.
|
||||
float tmpDamage = ((int)pdamage + DoneTotal) * (damagetype == DamageEffectType.DOT ? 1.0f : SpellDamagePctDone(victim, spellProto, damagetype));
|
||||
float tmpDamage = (float)((int)pdamage + DoneTotal) * DoneTotalMod;
|
||||
// apply spellmod to Done damage (flat and pct)
|
||||
Player _modOwner = GetSpellModOwner();
|
||||
if (_modOwner)
|
||||
{
|
||||
if (damagetype == DamageEffectType.DOT)
|
||||
_modOwner.ApplySpellMod(spellProto, SpellModOp.PeriodicHealingAndDamage, ref tmpDamage);
|
||||
else
|
||||
_modOwner.ApplySpellMod(spellProto, SpellModOp.HealingAndDamage, ref tmpDamage);
|
||||
}
|
||||
if (_modOwner != null)
|
||||
_modOwner.ApplySpellMod(spellProto, damagetype == DamageEffectType.DOT ? SpellModOp.PeriodicHealingAndDamage : SpellModOp.HealingAndDamage, ref tmpDamage);
|
||||
|
||||
return (uint)Math.Max(tmpDamage, 0.0f);
|
||||
}
|
||||
@@ -171,13 +166,21 @@ namespace Game.Entities
|
||||
if (spellProto == null || !victim || damagetype == DamageEffectType.Direct)
|
||||
return 1.0f;
|
||||
|
||||
// Some spells don't benefit from done mods
|
||||
if (spellProto.HasAttribute(SpellAttr3.NoDoneBonus))
|
||||
return 1.0f;
|
||||
|
||||
// Some spells don't benefit from pct done mods
|
||||
if (spellProto.HasAttribute(SpellAttr6.IgnoreCasterDamageModifiers))
|
||||
return 1.0f;
|
||||
|
||||
// For totems pct done mods are calculated when its calculation is run on the player in SpellDamageBonusDone.
|
||||
if (IsTypeId(TypeId.Unit) && IsTotem())
|
||||
return 1.0f;
|
||||
// For totems get damage bonus from owner
|
||||
if (IsCreature() && IsTotem())
|
||||
{
|
||||
Unit owner = GetOwner();
|
||||
if (owner != null)
|
||||
return owner.SpellDamagePctDone(victim, spellProto, damagetype);
|
||||
}
|
||||
|
||||
// Done total percent damage auras
|
||||
float DoneTotalMod = 1.0f;
|
||||
@@ -392,19 +395,23 @@ namespace Game.Entities
|
||||
return GetTotalAuraModifierByMiscMask(AuraType.ModHealing, (int)schoolMask);
|
||||
}
|
||||
|
||||
public int SpellCriticalHealingBonus(SpellInfo spellProto, int damage, Unit victim)
|
||||
public static int SpellCriticalHealingBonus(Unit caster, SpellInfo spellProto, int damage, Unit victim)
|
||||
{
|
||||
// Calculate critical bonus
|
||||
int crit_bonus = damage;
|
||||
|
||||
// adds additional damage to critBonus (from talents)
|
||||
Player modOwner = GetSpellModOwner();
|
||||
if (modOwner != null)
|
||||
modOwner.ApplySpellMod(spellProto, SpellModOp.CritDamageAndHealing, ref crit_bonus);
|
||||
if (caster != null)
|
||||
{
|
||||
Player modOwner = caster.GetSpellModOwner();
|
||||
if (modOwner != null)
|
||||
modOwner.ApplySpellMod(spellProto, SpellModOp.CritDamageAndHealing, ref crit_bonus);
|
||||
}
|
||||
|
||||
damage += crit_bonus;
|
||||
|
||||
damage = (int)(damage * GetTotalAuraMultiplier(AuraType.ModCriticalHealingAmount));
|
||||
if (caster != null)
|
||||
damage = (int)(damage * caster.GetTotalAuraMultiplier(AuraType.ModCriticalHealingAmount));
|
||||
|
||||
return damage;
|
||||
}
|
||||
@@ -424,19 +431,20 @@ namespace Game.Entities
|
||||
return healamount;
|
||||
|
||||
int DoneTotal = 0;
|
||||
float DoneTotalMod = SpellHealingPctDone(victim, spellProto);
|
||||
|
||||
// done scripted mod (take it from owner)
|
||||
Unit owner1 = GetOwner() ?? this;
|
||||
var mOverrideClassScript = owner1.GetAuraEffectsByType(AuraType.OverrideClassScripts);
|
||||
foreach (var eff in mOverrideClassScript)
|
||||
foreach (var aurEff in mOverrideClassScript)
|
||||
{
|
||||
if (!eff.IsAffectingSpell(spellProto))
|
||||
if (!aurEff.IsAffectingSpell(spellProto))
|
||||
continue;
|
||||
|
||||
switch (eff.GetMiscValue())
|
||||
switch (aurEff.GetMiscValue())
|
||||
{
|
||||
case 3736: // Hateful Totem of the Third Wind / Increased Lesser Healing Wave / LK Arena (4/5/6) Totem of the Third Wind / Savage Totem of the Third Wind
|
||||
DoneTotal += eff.GetAmount();
|
||||
DoneTotal += aurEff.GetAmount();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -491,25 +499,32 @@ namespace Game.Entities
|
||||
DoneTotal = 0;
|
||||
}
|
||||
|
||||
// use float as more appropriate for negative values and percent applying
|
||||
float heal = (healamount + DoneTotal) * (damagetype == DamageEffectType.DOT ? 1.0f : SpellHealingPctDone(victim, spellProto));
|
||||
float heal = (float)((int)healamount + DoneTotal) * DoneTotalMod;
|
||||
|
||||
// apply spellmod to Done amount
|
||||
Player _modOwner = GetSpellModOwner();
|
||||
if (_modOwner)
|
||||
{
|
||||
if (damagetype == DamageEffectType.DOT)
|
||||
_modOwner.ApplySpellMod(spellProto, SpellModOp.PeriodicHealingAndDamage, ref heal);
|
||||
else
|
||||
_modOwner.ApplySpellMod(spellProto, SpellModOp.HealingAndDamage, ref heal);
|
||||
}
|
||||
_modOwner.ApplySpellMod(spellProto, damagetype == DamageEffectType.DOT ? SpellModOp.PeriodicHealingAndDamage : SpellModOp.HealingAndDamage, ref heal);
|
||||
|
||||
return (uint)Math.Max(heal, 0.0f);
|
||||
}
|
||||
|
||||
public float SpellHealingPctDone(Unit victim, SpellInfo spellProto)
|
||||
{
|
||||
// For totems pct done mods are calculated when its calculation is run on the player in SpellHealingBonusDone.
|
||||
if (IsTypeId(TypeId.Unit) && IsTotem())
|
||||
// For totems get healing bonus from owner
|
||||
if (IsCreature() && IsTotem())
|
||||
{
|
||||
Unit owner = GetOwner();
|
||||
if (owner != null)
|
||||
return owner.SpellHealingPctDone(victim, spellProto);
|
||||
}
|
||||
|
||||
// Some spells don't benefit from done mods
|
||||
if (spellProto.HasAttribute(SpellAttr3.NoDoneBonus))
|
||||
return 1.0f;
|
||||
|
||||
// Some spells don't benefit from done mods
|
||||
if (spellProto.HasAttribute(SpellAttr6.IgnoreHealingModifiers))
|
||||
return 1.0f;
|
||||
|
||||
// No bonus healing for potion spells
|
||||
@@ -647,40 +662,19 @@ namespace Game.Entities
|
||||
return (uint)Math.Max(heal, 0.0f);
|
||||
}
|
||||
|
||||
public bool IsSpellCrit(Unit victim, Spell spell, AuraEffect aurEff, SpellSchoolMask schoolMask, WeaponAttackType attackType = WeaponAttackType.BaseAttack)
|
||||
public float SpellCritChanceDone(SpellInfo spellInfo, SpellSchoolMask schoolMask, WeaponAttackType attackType = WeaponAttackType.BaseAttack)
|
||||
{
|
||||
return RandomHelper.randChance(GetUnitSpellCriticalChance(victim, spell, aurEff, schoolMask, attackType));
|
||||
}
|
||||
|
||||
public float GetUnitSpellCriticalChance(Unit victim, Spell spell, AuraEffect aurEff, SpellSchoolMask schoolMask, WeaponAttackType attackType = WeaponAttackType.BaseAttack)
|
||||
{
|
||||
SpellInfo spellProto = spell?.GetSpellInfo() ?? aurEff?.GetSpellInfo();
|
||||
//! Mobs can't crit with spells. Player Totems can
|
||||
//! Fire Elemental (from totem) can too - but this part is a hack and needs more research
|
||||
if (GetGUID().IsCreatureOrVehicle() && !(IsTotem() && GetOwnerGUID().IsPlayer()) && GetEntry() != 15438)
|
||||
//! Mobs can't crit with spells. (Except player controlled)
|
||||
if (IsCreature() && !GetSpellModOwner())
|
||||
return 0.0f;
|
||||
|
||||
// not critting spell
|
||||
if (spellProto.HasAttribute(SpellAttr2.CantCrit))
|
||||
if (spellInfo.HasAttribute(SpellAttr2.CantCrit))
|
||||
return 0.0f;
|
||||
|
||||
float crit_chance = 0.0f;
|
||||
switch (spellProto.DmgClass)
|
||||
switch (spellInfo.DmgClass)
|
||||
{
|
||||
case SpellDmgClass.None:
|
||||
// We need more spells to find a general way (if there is any)
|
||||
switch (spellProto.Id)
|
||||
{
|
||||
case 379: // Earth Shield
|
||||
case 33778: // Lifebloom Final Bloom
|
||||
case 64844: // Divine Hymn
|
||||
case 71607: // Item - Bauble of True Blood 10m
|
||||
case 71646: // Item - Bauble of True Blood 25m
|
||||
break;
|
||||
default:
|
||||
return 0.0f;
|
||||
}
|
||||
goto case SpellDmgClass.Magic;
|
||||
case SpellDmgClass.Magic:
|
||||
{
|
||||
if (schoolMask.HasAnyFlag(SpellSchoolMask.Normal))
|
||||
@@ -690,26 +684,58 @@ namespace Game.Entities
|
||||
crit_chance = ToPlayer().m_activePlayerData.SpellCritPercentage;
|
||||
else
|
||||
crit_chance = BaseSpellCritChance;
|
||||
break;
|
||||
}
|
||||
case SpellDmgClass.Melee:
|
||||
case SpellDmgClass.Ranged:
|
||||
crit_chance += GetUnitCriticalChanceDone(attackType);
|
||||
break;
|
||||
|
||||
case SpellDmgClass.None:
|
||||
default:
|
||||
return 0f;
|
||||
}
|
||||
// percent done
|
||||
// only players use intelligence for critical chance computations
|
||||
Player modOwner = GetSpellModOwner();
|
||||
if (modOwner != null)
|
||||
modOwner.ApplySpellMod(spellInfo, SpellModOp.CritChance, ref crit_chance);
|
||||
|
||||
return Math.Max(crit_chance, 0.0f);
|
||||
}
|
||||
|
||||
public float SpellCritChanceTaken(Unit caster, Spell spell, AuraEffect aurEff, SpellSchoolMask schoolMask, float doneChance, WeaponAttackType attackType = WeaponAttackType.BaseAttack)
|
||||
{
|
||||
SpellInfo spellInfo = spell != null ? spell.GetSpellInfo() : aurEff.GetSpellInfo();
|
||||
// not critting spell
|
||||
if (spellInfo.HasAttribute(SpellAttr2.CantCrit))
|
||||
return 0.0f;
|
||||
|
||||
float crit_chance = doneChance;
|
||||
switch (spellInfo.DmgClass)
|
||||
{
|
||||
case SpellDmgClass.Magic:
|
||||
{
|
||||
// taken
|
||||
if (victim)
|
||||
if (!spellInfo.IsPositive())
|
||||
{
|
||||
// Modify critical chance by victim SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE
|
||||
crit_chance += GetTotalAuraModifier(AuraType.ModAttackerSpellAndWeaponCritChance);
|
||||
}
|
||||
|
||||
if (caster)
|
||||
{
|
||||
if (!spellProto.IsPositive())
|
||||
{
|
||||
// Modify critical chance by victim SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE
|
||||
crit_chance += victim.GetTotalAuraModifier(AuraType.ModAttackerSpellAndWeaponCritChance);
|
||||
}
|
||||
// scripted (increase crit chance ... against ... target by x%
|
||||
var mOverrideClassScript = GetAuraEffectsByType(AuraType.OverrideClassScripts);
|
||||
var mOverrideClassScript = caster.GetAuraEffectsByType(AuraType.OverrideClassScripts);
|
||||
foreach (var eff in mOverrideClassScript)
|
||||
{
|
||||
if (!eff.IsAffectingSpell(spellProto))
|
||||
if (!eff.IsAffectingSpell(spellInfo))
|
||||
continue;
|
||||
|
||||
switch (eff.GetMiscValue())
|
||||
{
|
||||
case 911: // Shatter
|
||||
if (victim.HasAuraState(AuraStateType.Frozen, spellProto, this))
|
||||
if (HasAuraState(AuraStateType.Frozen, spellInfo, this))
|
||||
{
|
||||
crit_chance *= 1.5f;
|
||||
AuraEffect _eff = eff.GetBase().GetEffect(1);
|
||||
@@ -722,19 +748,19 @@ namespace Game.Entities
|
||||
}
|
||||
}
|
||||
// Custom crit by class
|
||||
switch (spellProto.SpellFamilyName)
|
||||
switch (spellInfo.SpellFamilyName)
|
||||
{
|
||||
case SpellFamilyNames.Rogue:
|
||||
// Shiv-applied poisons can't crit
|
||||
if (FindCurrentSpellBySpellId(5938) != null)
|
||||
if (caster.FindCurrentSpellBySpellId(5938) != null)
|
||||
crit_chance = 0.0f;
|
||||
break;
|
||||
case SpellFamilyNames.Shaman:
|
||||
// Lava Burst
|
||||
if (spellProto.SpellFamilyFlags[1].HasAnyFlag(0x00001000u))
|
||||
if (spellInfo.SpellFamilyFlags[1].HasAnyFlag(0x00001000u))
|
||||
{
|
||||
if (victim.GetAuraEffect(AuraType.PeriodicDamage, SpellFamilyNames.Shaman, new FlagArray128(0x10000000, 0, 0), GetGUID()) != null)
|
||||
if (victim.GetTotalAuraModifier(AuraType.ModAttackerSpellAndWeaponCritChance) > -100)
|
||||
if (GetAuraEffect(AuraType.PeriodicDamage, SpellFamilyNames.Shaman, new FlagArray128(0x10000000, 0, 0), caster.GetGUID()) != null)
|
||||
if (GetTotalAuraModifier(AuraType.ModAttackerSpellAndWeaponCritChance) > -100)
|
||||
return 100.0f;
|
||||
break;
|
||||
}
|
||||
@@ -742,9 +768,9 @@ namespace Game.Entities
|
||||
}
|
||||
|
||||
// Spell crit suppression
|
||||
if (victim.GetTypeId() == TypeId.Unit)
|
||||
if (IsCreature())
|
||||
{
|
||||
int levelDiff = (int)(victim.GetLevelForTarget(this) - GetLevel());
|
||||
int levelDiff = (int)(GetLevelForTarget(this) - caster.GetLevel());
|
||||
crit_chance -= levelDiff * 1.0f;
|
||||
}
|
||||
}
|
||||
@@ -753,38 +779,34 @@ namespace Game.Entities
|
||||
case SpellDmgClass.Melee:
|
||||
case SpellDmgClass.Ranged:
|
||||
{
|
||||
if (victim)
|
||||
crit_chance += GetUnitCriticalChance(attackType, victim);
|
||||
if (caster != null)
|
||||
crit_chance += GetUnitCriticalChanceTaken(caster, attackType, crit_chance);
|
||||
break;
|
||||
}
|
||||
case SpellDmgClass.None:
|
||||
default:
|
||||
return 0.0f;
|
||||
return 0f;
|
||||
}
|
||||
// percent done
|
||||
// only players use intelligence for critical chance computations
|
||||
Player modOwner = GetSpellModOwner();
|
||||
if (modOwner != null)
|
||||
modOwner.ApplySpellMod(spellProto, SpellModOp.CritChance, ref crit_chance);
|
||||
|
||||
// for this types the bonus was already added in GetUnitCriticalChance, do not add twice
|
||||
if (spellProto.DmgClass != SpellDmgClass.Melee && spellProto.DmgClass != SpellDmgClass.Ranged)
|
||||
if (caster != null && spellInfo.DmgClass != SpellDmgClass.Melee && spellInfo.DmgClass != SpellDmgClass.Ranged)
|
||||
{
|
||||
crit_chance += victim.GetTotalAuraModifier(AuraType.ModCritChanceForCasterWithAbilities, aurEff => aurEff.GetCasterGUID() == GetGUID() && aurEff.IsAffectingSpell(spellProto));
|
||||
crit_chance += GetTotalAuraModifier(AuraType.ModCritChanceForCasterWithAbilities, aurEff => aurEff.GetCasterGUID() == caster.GetGUID() && aurEff.IsAffectingSpell(spellInfo));
|
||||
|
||||
crit_chance += victim.GetTotalAuraModifier(AuraType.ModCritChanceForCaster, aurEff => aurEff.GetCasterGUID() != GetGUID());
|
||||
crit_chance += GetTotalAuraModifier(AuraType.ModCritChanceForCaster, aurEff => aurEff.GetCasterGUID() == caster.GetGUID());
|
||||
|
||||
crit_chance += GetTotalAuraModifier(AuraType.ModCritChanceVersusTargetHealth, aurEff => !victim.HealthBelowPct(aurEff.GetMiscValueB()));
|
||||
crit_chance += caster.GetTotalAuraModifier(AuraType.ModCritChanceVersusTargetHealth, aurEff => !HealthBelowPct(aurEff.GetMiscValueB()));
|
||||
|
||||
TempSummon tempSummon = ToTempSummon();
|
||||
TempSummon tempSummon = caster.ToTempSummon();
|
||||
if (tempSummon != null)
|
||||
crit_chance += victim.GetTotalAuraModifier(AuraType.ModCritChanceForCasterPet, aurEff => aurEff.GetCasterGUID() == tempSummon.GetSummonerGUID());
|
||||
crit_chance += GetTotalAuraModifier(AuraType.ModCritChanceForCasterPet, aurEff => aurEff.GetCasterGUID() == tempSummon.GetSummonerGUID());
|
||||
}
|
||||
|
||||
// call script handlers
|
||||
if (spell)
|
||||
spell.CallScriptCalcCritChanceHandlers(victim, ref crit_chance);
|
||||
spell.CallScriptCalcCritChanceHandlers(this, ref crit_chance);
|
||||
else
|
||||
aurEff.GetBase().CallScriptEffectCalcCritChanceHandlers(aurEff, aurEff.GetBase().GetApplicationOfTarget(victim.GetGUID()), victim, ref crit_chance);
|
||||
aurEff.GetBase().CallScriptEffectCalcCritChanceHandlers(aurEff, aurEff.GetBase().GetApplicationOfTarget(GetGUID()), this, ref crit_chance);
|
||||
|
||||
return Math.Max(crit_chance, 0.0f);
|
||||
}
|
||||
@@ -1752,16 +1774,17 @@ namespace Game.Entities
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ProcSkillsAndAuras(Unit actionTarget, ProcFlags typeMaskActor, ProcFlags typeMaskActionTarget, ProcFlagsSpellType spellTypeMask, ProcFlagsSpellPhase spellPhaseMask, ProcFlagsHit hitMask, Spell spell, DamageInfo damageInfo, HealInfo healInfo)
|
||||
public static void ProcSkillsAndAuras(Unit actor, Unit actionTarget, ProcFlags typeMaskActor, ProcFlags typeMaskActionTarget, ProcFlagsSpellType spellTypeMask, ProcFlagsSpellPhase spellPhaseMask, ProcFlagsHit hitMask, Spell spell, DamageInfo damageInfo, HealInfo healInfo)
|
||||
{
|
||||
WeaponAttackType attType = damageInfo != null ? damageInfo.GetAttackType() : WeaponAttackType.BaseAttack;
|
||||
if (typeMaskActor != 0)
|
||||
ProcSkillsAndReactives(false, actionTarget, typeMaskActor, hitMask, attType);
|
||||
if (typeMaskActor != 0 && actor != null)
|
||||
actor.ProcSkillsAndReactives(false, actionTarget, typeMaskActor, hitMask, attType);
|
||||
|
||||
if (typeMaskActionTarget != 0 && actionTarget)
|
||||
actionTarget.ProcSkillsAndReactives(true, this, typeMaskActionTarget, hitMask, attType);
|
||||
actionTarget.ProcSkillsAndReactives(true, actor, typeMaskActionTarget, hitMask, attType);
|
||||
|
||||
TriggerAurasProcOnEvent(null, null, actionTarget, typeMaskActor, typeMaskActionTarget, spellTypeMask, spellPhaseMask, hitMask, spell, damageInfo, healInfo);
|
||||
if (actor != null)
|
||||
actor.TriggerAurasProcOnEvent(null, null, actionTarget, typeMaskActor, typeMaskActionTarget, spellTypeMask, spellPhaseMask, hitMask, spell, damageInfo, healInfo);
|
||||
}
|
||||
|
||||
void ProcSkillsAndReactives(bool isVictim, Unit procTarget, ProcFlags typeMask, ProcFlagsHit hitMask, WeaponAttackType attType)
|
||||
@@ -2125,30 +2148,33 @@ namespace Game.Entities
|
||||
return false;
|
||||
}
|
||||
|
||||
public uint SpellCriticalDamageBonus(SpellInfo spellProto, uint damage, Unit victim = null)
|
||||
public static uint SpellCriticalDamageBonus(Unit caster, SpellInfo spellProto, uint damage, Unit victim = null)
|
||||
{
|
||||
// Calculate critical bonus
|
||||
int crit_bonus = (int)damage * 2;
|
||||
float crit_mod = 0.0f;
|
||||
|
||||
crit_mod += (GetTotalAuraMultiplierByMiscMask(AuraType.ModCritDamageBonus, (uint)spellProto.GetSchoolMask()) - 1.0f) * 100;
|
||||
|
||||
if (crit_bonus != 0)
|
||||
MathFunctions.AddPct(ref crit_bonus, (int)crit_mod);
|
||||
|
||||
MathFunctions.AddPct(ref crit_bonus, victim.GetTotalAuraModifier(AuraType.ModCriticalDamageTakenFromCaster, aurEff =>
|
||||
if (caster != null)
|
||||
{
|
||||
return aurEff.GetCasterGUID() == GetGUID();
|
||||
}));
|
||||
crit_mod += (caster.GetTotalAuraMultiplierByMiscMask(AuraType.ModCritDamageBonus, (uint)spellProto.GetSchoolMask()) - 1.0f) * 100;
|
||||
|
||||
crit_bonus -= (int)damage;
|
||||
if (crit_bonus != 0)
|
||||
MathFunctions.AddPct(ref crit_bonus, (int)crit_mod);
|
||||
|
||||
// adds additional damage to critBonus (from talents)
|
||||
Player modOwner = GetSpellModOwner();
|
||||
if (modOwner != null)
|
||||
modOwner.ApplySpellMod(spellProto, SpellModOp.CritDamageAndHealing, ref crit_bonus);
|
||||
MathFunctions.AddPct(ref crit_bonus, victim.GetTotalAuraModifier(AuraType.ModCriticalDamageTakenFromCaster, aurEff =>
|
||||
{
|
||||
return aurEff.GetCasterGUID() == caster.GetGUID();
|
||||
}));
|
||||
|
||||
crit_bonus += (int)damage;
|
||||
crit_bonus -= (int)damage;
|
||||
|
||||
// adds additional damage to critBonus (from talents)
|
||||
Player modOwner = caster.GetSpellModOwner();
|
||||
if (modOwner != null)
|
||||
modOwner.ApplySpellMod(spellProto, SpellModOp.CritDamageAndHealing, ref crit_bonus);
|
||||
|
||||
crit_bonus += (int)damage;
|
||||
}
|
||||
|
||||
return (uint)crit_bonus;
|
||||
}
|
||||
@@ -2197,46 +2223,53 @@ namespace Game.Entities
|
||||
return spellInfo.GetSpellSpecific() == SpellSpecificType.MagePolymorph;
|
||||
}
|
||||
|
||||
public void DealHeal(HealInfo healInfo)
|
||||
public static void DealHeal(HealInfo healInfo)
|
||||
{
|
||||
int gain = 0;
|
||||
Unit healer = healInfo.GetHealer();
|
||||
Unit victim = healInfo.GetTarget();
|
||||
uint addhealth = healInfo.GetHeal();
|
||||
|
||||
if (victim.IsAIEnabled)
|
||||
victim.GetAI().HealReceived(this, addhealth);
|
||||
if (healer)
|
||||
{
|
||||
if (victim.IsAIEnabled)
|
||||
victim.GetAI().HealReceived(healer, addhealth);
|
||||
|
||||
if (IsAIEnabled)
|
||||
GetAI().HealDone(victim, addhealth);
|
||||
if (healer.IsAIEnabled)
|
||||
healer.GetAI().HealDone(victim, addhealth);
|
||||
}
|
||||
|
||||
if (addhealth != 0)
|
||||
gain = (int)victim.ModifyHealth(addhealth);
|
||||
|
||||
// Hook for OnHeal Event
|
||||
uint tempGain = (uint)gain;
|
||||
Global.ScriptMgr.OnHeal(this, victim, ref tempGain);
|
||||
Global.ScriptMgr.OnHeal(healer, victim, ref tempGain);
|
||||
gain = (int)tempGain;
|
||||
|
||||
Unit unit = this;
|
||||
Unit unit = healer;
|
||||
if (healer != null && healer.IsCreature() && healer.IsTotem())
|
||||
unit = healer.GetOwner();
|
||||
|
||||
if (IsTypeId(TypeId.Unit) && IsTotem())
|
||||
unit = GetOwner();
|
||||
|
||||
Player player = unit.ToPlayer();
|
||||
if (player != null)
|
||||
if (unit)
|
||||
{
|
||||
Battleground bg = player.GetBattleground();
|
||||
if (bg)
|
||||
bg.UpdatePlayerScore(player, ScoreType.HealingDone, (uint)gain);
|
||||
Player bgPlayer = unit.ToPlayer();
|
||||
if (bgPlayer != null)
|
||||
{
|
||||
Battleground bg = bgPlayer.GetBattleground();
|
||||
if (bg)
|
||||
bg.UpdatePlayerScore(bgPlayer, ScoreType.HealingDone, (uint)gain);
|
||||
|
||||
// use the actual gain, as the overheal shall not be counted, skip gain 0 (it ignored anyway in to criteria)
|
||||
if (gain != 0)
|
||||
player.UpdateCriteria(CriteriaTypes.HealingDone, (uint)gain, 0, 0, victim);
|
||||
// use the actual gain, as the overheal shall not be counted, skip gain 0 (it ignored anyway in to criteria)
|
||||
if (gain != 0)
|
||||
bgPlayer.UpdateCriteria(CriteriaTypes.HealingDone, (uint)gain, 0, 0, victim);
|
||||
|
||||
player.UpdateCriteria(CriteriaTypes.HighestHealCasted, addhealth);
|
||||
bgPlayer.UpdateCriteria(CriteriaTypes.HighestHealCasted, addhealth);
|
||||
}
|
||||
}
|
||||
|
||||
if ((player = victim.ToPlayer()) != null)
|
||||
Player player = victim.ToPlayer();
|
||||
if (player != null)
|
||||
{
|
||||
player.UpdateCriteria(CriteriaTypes.TotalHealingReceived, (uint)gain);
|
||||
player.UpdateCriteria(CriteriaTypes.HighestHealingReceived, addhealth);
|
||||
@@ -2267,8 +2300,8 @@ namespace Game.Entities
|
||||
{
|
||||
// calculate heal absorb and reduce healing
|
||||
CalcHealAbsorb(healInfo);
|
||||
|
||||
DealHeal(healInfo);
|
||||
|
||||
SendHealSpellLog(healInfo, critical);
|
||||
return healInfo.GetEffectiveHeal();
|
||||
}
|
||||
@@ -2329,84 +2362,90 @@ namespace Game.Entities
|
||||
|
||||
SpellSchoolMask damageSchoolMask = damageInfo.schoolMask;
|
||||
|
||||
if (IsDamageReducedByArmor(damageSchoolMask, spellInfo))
|
||||
damage = (int)CalcArmorReducedDamage(damageInfo.attacker, victim, (uint)damage, spellInfo, attackType);
|
||||
|
||||
bool blocked = false;
|
||||
// Per-school calc
|
||||
switch (spellInfo.DmgClass)
|
||||
// Spells with SPELL_ATTR4_FIXED_DAMAGE ignore resilience because their damage is based off another spell's damage.
|
||||
if (!spellInfo.HasAttribute(SpellAttr4.FixedDamage))
|
||||
{
|
||||
// Melee and Ranged Spells
|
||||
case SpellDmgClass.Ranged:
|
||||
case SpellDmgClass.Melee:
|
||||
{
|
||||
// Physical Damage
|
||||
if (damageSchoolMask.HasAnyFlag(SpellSchoolMask.Normal))
|
||||
if (IsDamageReducedByArmor(damageSchoolMask, spellInfo))
|
||||
damage = (int)CalcArmorReducedDamage(damageInfo.attacker, victim, (uint)damage, spellInfo, attackType);
|
||||
|
||||
bool blocked = false;
|
||||
// Per-school calc
|
||||
switch (spellInfo.DmgClass)
|
||||
{
|
||||
// Melee and Ranged Spells
|
||||
case SpellDmgClass.Ranged:
|
||||
case SpellDmgClass.Melee:
|
||||
{
|
||||
// Spells with this attribute were already calculated in MeleeSpellHitResult
|
||||
if (!spellInfo.HasAttribute(SpellAttr3.BlockableSpell))
|
||||
// Physical Damage
|
||||
if (damageSchoolMask.HasAnyFlag(SpellSchoolMask.Normal))
|
||||
{
|
||||
// Get blocked status
|
||||
blocked = IsSpellBlocked(victim, spellInfo, attackType);
|
||||
// Spells with this attribute were already calculated in MeleeSpellHitResult
|
||||
if (!spellInfo.HasAttribute(SpellAttr3.BlockableSpell))
|
||||
{
|
||||
// Get blocked status
|
||||
blocked = IsSpellBlocked(victim, spellInfo, attackType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (crit)
|
||||
{
|
||||
damageInfo.HitInfo |= HitInfo.CriticalHit;
|
||||
|
||||
// Calculate crit bonus
|
||||
uint crit_bonus = (uint)damage;
|
||||
// Apply crit_damage bonus for melee spells
|
||||
Player modOwner = GetSpellModOwner();
|
||||
if (modOwner != null)
|
||||
modOwner.ApplySpellMod(spellInfo, SpellModOp.CritDamageAndHealing, ref crit_bonus);
|
||||
damage += (int)crit_bonus;
|
||||
|
||||
// Increase crit damage from SPELL_AURA_MOD_CRIT_DAMAGE_BONUS
|
||||
float critPctDamageMod = (GetTotalAuraMultiplierByMiscMask(AuraType.ModCritDamageBonus, (uint)spellInfo.GetSchoolMask()) - 1.0f) * 100;
|
||||
|
||||
if (critPctDamageMod != 0)
|
||||
MathFunctions.AddPct(ref damage, (int)critPctDamageMod);
|
||||
}
|
||||
|
||||
// Spell weapon based damage CAN BE crit & blocked at same time
|
||||
if (blocked)
|
||||
{
|
||||
// double blocked amount if block is critical
|
||||
float value = victim.GetBlockPercent(GetLevel());
|
||||
if (victim.IsBlockCritical())
|
||||
value *= 2; // double blocked percent
|
||||
damageInfo.blocked = (uint)MathFunctions.CalculatePct(damage, value);
|
||||
if (damage <= damageInfo.blocked)
|
||||
if (crit)
|
||||
{
|
||||
damageInfo.blocked = (uint)damage;
|
||||
damageInfo.fullBlock = true;
|
||||
damageInfo.HitInfo |= HitInfo.CriticalHit;
|
||||
|
||||
// Calculate crit bonus
|
||||
uint crit_bonus = (uint)damage;
|
||||
// Apply crit_damage bonus for melee spells
|
||||
Player modOwner = GetSpellModOwner();
|
||||
if (modOwner != null)
|
||||
modOwner.ApplySpellMod(spellInfo, SpellModOp.CritDamageAndHealing, ref crit_bonus);
|
||||
damage += (int)crit_bonus;
|
||||
|
||||
// Increase crit damage from SPELL_AURA_MOD_CRIT_DAMAGE_BONUS
|
||||
float critPctDamageMod = (GetTotalAuraMultiplierByMiscMask(AuraType.ModCritDamageBonus, (uint)spellInfo.GetSchoolMask()) - 1.0f) * 100;
|
||||
|
||||
if (critPctDamageMod != 0)
|
||||
MathFunctions.AddPct(ref damage, (int)critPctDamageMod);
|
||||
}
|
||||
damage -= (int)damageInfo.blocked;
|
||||
|
||||
// Spell weapon based damage CAN BE crit & blocked at same time
|
||||
if (blocked)
|
||||
{
|
||||
// double blocked amount if block is critical
|
||||
float value = victim.GetBlockPercent(GetLevel());
|
||||
if (victim.IsBlockCritical())
|
||||
value *= 2; // double blocked percent
|
||||
damageInfo.blocked = (uint)MathFunctions.CalculatePct(damage, value);
|
||||
if (damage <= damageInfo.blocked)
|
||||
{
|
||||
damageInfo.blocked = (uint)damage;
|
||||
damageInfo.fullBlock = true;
|
||||
}
|
||||
damage -= (int)damageInfo.blocked;
|
||||
}
|
||||
|
||||
if (CanApplyResilience())
|
||||
ApplyResilience(victim, ref damage);
|
||||
|
||||
break;
|
||||
}
|
||||
uint dmg = (uint)damage;
|
||||
ApplyResilience(victim, ref dmg);
|
||||
damage = (int)dmg;
|
||||
break;
|
||||
}
|
||||
// Magical Attacks
|
||||
case SpellDmgClass.None:
|
||||
case SpellDmgClass.Magic:
|
||||
{
|
||||
// If crit add critical bonus
|
||||
if (crit)
|
||||
// Magical Attacks
|
||||
case SpellDmgClass.None:
|
||||
case SpellDmgClass.Magic:
|
||||
{
|
||||
damageInfo.HitInfo |= HitInfo.CriticalHit;
|
||||
damage = (int)SpellCriticalDamageBonus(spellInfo, (uint)damage, victim);
|
||||
// If crit add critical bonus
|
||||
if (crit)
|
||||
{
|
||||
damageInfo.HitInfo |= HitInfo.CriticalHit;
|
||||
damage = (int)SpellCriticalDamageBonus(this, spellInfo, (uint)damage, victim);
|
||||
}
|
||||
|
||||
if (CanApplyResilience())
|
||||
ApplyResilience(victim, ref damage);
|
||||
|
||||
break;
|
||||
}
|
||||
uint dmg = (uint)damage;
|
||||
ApplyResilience(victim, ref dmg);
|
||||
damage = (int)dmg;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Script Hook For CalculateSpellDamageTaken -- Allow scripts to change the Damage post class mitigation calculations
|
||||
@@ -2431,6 +2470,7 @@ namespace Game.Entities
|
||||
|
||||
damageInfo.damage = dmgInfo.GetDamage();
|
||||
}
|
||||
|
||||
public void DealSpellDamage(SpellNonMeleeDamage damageInfo, bool durabilityLoss)
|
||||
{
|
||||
if (damageInfo == null)
|
||||
@@ -2451,7 +2491,7 @@ namespace Game.Entities
|
||||
|
||||
// Call default DealDamage
|
||||
CleanDamage cleanDamage = new(damageInfo.cleanDamage, damageInfo.absorb, WeaponAttackType.BaseAttack, MeleeHitOutcome.Normal);
|
||||
DealDamage(victim, damageInfo.damage, cleanDamage, DamageEffectType.SpellDirect, damageInfo.schoolMask, damageInfo.Spell, durabilityLoss);
|
||||
DealDamage(this, victim, damageInfo.damage, cleanDamage, DamageEffectType.SpellDirect, damageInfo.schoolMask, damageInfo.Spell, durabilityLoss);
|
||||
}
|
||||
|
||||
public void SendSpellNonMeleeDamageLog(SpellNonMeleeDamage log)
|
||||
@@ -4181,6 +4221,7 @@ namespace Game.Entities
|
||||
else
|
||||
aurApp._HandleEffect(effIndex, true);
|
||||
}
|
||||
|
||||
// handles effects of aura application
|
||||
// should be done after registering aura in lists
|
||||
public void _ApplyAura(AuraApplication aurApp, uint effMask)
|
||||
@@ -4219,7 +4260,6 @@ namespace Game.Entities
|
||||
if (aurApp.HasRemoveMode())
|
||||
return;
|
||||
|
||||
aura.HandleAuraSpecificPeriodics(aurApp, caster);
|
||||
aura.HandleAuraSpecificMods(aurApp, caster, true, false);
|
||||
|
||||
// apply effects of the aura
|
||||
@@ -4229,6 +4269,7 @@ namespace Game.Entities
|
||||
aurApp._HandleEffect(i, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void _AddAura(UnitAura aura, Unit caster)
|
||||
{
|
||||
Cypher.Assert(!m_cleanupDone);
|
||||
@@ -4261,6 +4302,7 @@ namespace Game.Entities
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Aura _TryStackingOrRefreshingExistingAura(SpellInfo newAura, uint effMask, Unit caster, int[] baseAmount = null, Item castItem = null, ObjectGuid casterGUID = default, bool resetPeriodicTimer = true, ObjectGuid castItemGuid = default, uint castItemId = 0, int castItemLevel = -1)
|
||||
{
|
||||
Cypher.Assert(!casterGUID.IsEmpty() || caster);
|
||||
|
||||
+281
-260
@@ -2388,11 +2388,12 @@ namespace Game.Entities
|
||||
RemoveDynamicUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.ChannelObjects), index);
|
||||
}
|
||||
|
||||
public bool IsDamageReducedByArmor(SpellSchoolMask schoolMask, SpellInfo spellInfo = null, sbyte effIndex = -1)
|
||||
public static bool IsDamageReducedByArmor(SpellSchoolMask schoolMask, SpellInfo spellInfo = null, sbyte effIndex = -1)
|
||||
{
|
||||
// only physical spells damage gets reduced by armor
|
||||
if ((schoolMask & SpellSchoolMask.Normal) == 0)
|
||||
return false;
|
||||
|
||||
if (spellInfo != null)
|
||||
{
|
||||
// there are spells with no specific attribute but they have "ignores armor" in tooltip
|
||||
@@ -2411,6 +2412,7 @@ namespace Game.Entities
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2542,7 +2544,7 @@ namespace Game.Entities
|
||||
|
||||
void StartReactiveTimer(ReactiveType reactive) { m_reactiveTimer[reactive] = 4000; }
|
||||
|
||||
void DealDamageMods(Unit victim, ref uint damage)
|
||||
public static void DealDamageMods(Unit attacker, Unit victim, ref uint damage)
|
||||
{
|
||||
if (victim == null || !victim.IsAlive() || victim.HasUnitState(UnitState.InFlight)
|
||||
|| (victim.IsTypeId(TypeId.Unit) && victim.ToCreature().IsInEvadeMode()))
|
||||
@@ -2550,7 +2552,8 @@ namespace Game.Entities
|
||||
damage = 0;
|
||||
}
|
||||
}
|
||||
public void DealDamageMods(Unit victim, ref uint damage, ref uint absorb)
|
||||
|
||||
public static void DealDamageMods(Unit attacker, Unit victim, ref uint damage, ref uint absorb)
|
||||
{
|
||||
if (victim == null || !victim.IsAlive() || victim.HasUnitState(UnitState.InFlight)
|
||||
|| (victim.IsTypeId(TypeId.Unit) && victim.ToCreature().IsEvadingAttacks()))
|
||||
@@ -2560,156 +2563,22 @@ namespace Game.Entities
|
||||
return;
|
||||
}
|
||||
|
||||
damage = (uint)(damage * GetDamageMultiplierForTarget(victim));
|
||||
if (attacker != null)
|
||||
damage *= (uint)attacker.GetDamageMultiplierForTarget(victim);
|
||||
}
|
||||
void DealMeleeDamage(CalcDamageInfo damageInfo, bool durabilityLoss)
|
||||
{
|
||||
Unit victim = damageInfo.target;
|
||||
|
||||
if (!victim.IsAlive() || victim.HasUnitState(UnitState.InFlight) || (victim.IsTypeId(TypeId.Unit) && victim.ToCreature().IsEvadingAttacks()))
|
||||
return;
|
||||
|
||||
// Hmmmm dont like this emotes client must by self do all animations
|
||||
if (damageInfo.HitInfo.HasAnyFlag(HitInfo.CriticalHit))
|
||||
victim.HandleEmoteCommand(Emote.OneshotWoundCritical);
|
||||
if (damageInfo.blocked_amount != 0 && damageInfo.TargetState != VictimState.Blocks)
|
||||
victim.HandleEmoteCommand(Emote.OneshotParryShield);
|
||||
|
||||
if (damageInfo.TargetState == VictimState.Parry &&
|
||||
(!IsTypeId(TypeId.Unit) || !ToCreature().GetCreatureTemplate().FlagsExtra.HasAnyFlag(CreatureFlagsExtra.NoParryHasten)))
|
||||
{
|
||||
// Get attack timers
|
||||
float offtime = victim.GetAttackTimer(WeaponAttackType.OffAttack);
|
||||
float basetime = victim.GetAttackTimer(WeaponAttackType.BaseAttack);
|
||||
// Reduce attack time
|
||||
if (victim.HaveOffhandWeapon() && offtime < basetime)
|
||||
{
|
||||
float percent20 = victim.GetBaseAttackTime(WeaponAttackType.OffAttack) * 0.20f;
|
||||
float percent60 = 3.0f * percent20;
|
||||
if (offtime > percent20 && offtime <= percent60)
|
||||
victim.SetAttackTimer(WeaponAttackType.OffAttack, (uint)percent20);
|
||||
else if (offtime > percent60)
|
||||
{
|
||||
offtime -= 2.0f * percent20;
|
||||
victim.SetAttackTimer(WeaponAttackType.OffAttack, (uint)offtime);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float percent20 = victim.GetBaseAttackTime(WeaponAttackType.BaseAttack) * 0.20f;
|
||||
float percent60 = 3.0f * percent20;
|
||||
if (basetime > percent20 && basetime <= percent60)
|
||||
victim.SetAttackTimer(WeaponAttackType.BaseAttack, (uint)percent20);
|
||||
else if (basetime > percent60)
|
||||
{
|
||||
basetime -= 2.0f * percent20;
|
||||
victim.SetAttackTimer(WeaponAttackType.BaseAttack, (uint)basetime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call default DealDamage
|
||||
CleanDamage cleanDamage = new(damageInfo.cleanDamage, damageInfo.absorb, damageInfo.attackType, damageInfo.hitOutCome);
|
||||
DealDamage(victim, damageInfo.damage, cleanDamage, DamageEffectType.Direct, (SpellSchoolMask)damageInfo.damageSchoolMask, null, durabilityLoss);
|
||||
|
||||
// If this is a creature and it attacks from behind it has a probability to daze it's victim
|
||||
if ((damageInfo.hitOutCome == MeleeHitOutcome.Crit || damageInfo.hitOutCome == MeleeHitOutcome.Crushing || damageInfo.hitOutCome == MeleeHitOutcome.Normal || damageInfo.hitOutCome == MeleeHitOutcome.Glancing) &&
|
||||
!IsTypeId(TypeId.Player) && !ToCreature().IsControlledByPlayer() && !victim.HasInArc(MathFunctions.PI, this)
|
||||
&& (victim.IsTypeId(TypeId.Player) || !victim.ToCreature().IsWorldBoss()) && !victim.IsVehicle())
|
||||
{
|
||||
// 20% base chance
|
||||
float chance = 20.0f;
|
||||
|
||||
// there is a newbie protection, at level 10 just 7% base chance; assuming linear function
|
||||
if (victim.GetLevel() < 30)
|
||||
chance = 0.65f * victim.GetLevelForTarget(this) + 0.5f;
|
||||
|
||||
uint victimDefense = victim.GetMaxSkillValueForLevel(this);
|
||||
uint attackerMeleeSkill = GetMaxSkillValueForLevel();
|
||||
|
||||
chance *= attackerMeleeSkill / (float)victimDefense * 0.16f;
|
||||
|
||||
// -probability is between 0% and 40%
|
||||
MathFunctions.RoundToInterval(ref chance, 0.0f, 40.0f);
|
||||
|
||||
if (RandomHelper.randChance(chance))
|
||||
CastSpell(victim, 1604, true);
|
||||
}
|
||||
|
||||
if (IsTypeId(TypeId.Player))
|
||||
{
|
||||
DamageInfo dmgInfo = new(damageInfo);
|
||||
ToPlayer().CastItemCombatSpell(dmgInfo);
|
||||
}
|
||||
|
||||
// Do effect if any damage done to target
|
||||
if (damageInfo.damage != 0)
|
||||
{
|
||||
// We're going to call functions which can modify content of the list during iteration over it's elements
|
||||
// Let's copy the list so we can prevent iterator invalidation
|
||||
var vDamageShieldsCopy = victim.GetAuraEffectsByType(AuraType.DamageShield);
|
||||
foreach (var dmgShield in vDamageShieldsCopy)
|
||||
{
|
||||
SpellInfo spellInfo = dmgShield.GetSpellInfo();
|
||||
|
||||
// Damage shield can be resisted...
|
||||
var missInfo = victim.SpellHitResult(this, spellInfo, false);
|
||||
if (missInfo != SpellMissInfo.None)
|
||||
{
|
||||
victim.SendSpellMiss(this, spellInfo.Id, missInfo);
|
||||
continue;
|
||||
}
|
||||
|
||||
// ...or immuned
|
||||
if (IsImmunedToDamage(spellInfo))
|
||||
{
|
||||
victim.SendSpellDamageImmune(this, spellInfo.Id, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint damage = (uint)dmgShield.GetAmount();
|
||||
Unit caster = dmgShield.GetCaster();
|
||||
if (caster)
|
||||
{
|
||||
damage = caster.SpellDamageBonusDone(this, spellInfo, damage, DamageEffectType.SpellDirect, dmgShield.GetSpellEffectInfo());
|
||||
damage = SpellDamageBonusTaken(caster, spellInfo, damage, DamageEffectType.SpellDirect, dmgShield.GetSpellEffectInfo());
|
||||
}
|
||||
|
||||
DamageInfo damageInfo1 = new(this, victim, damage, spellInfo, spellInfo.GetSchoolMask(), DamageEffectType.SpellDirect, WeaponAttackType.BaseAttack);
|
||||
victim.CalcAbsorbResist(damageInfo1);
|
||||
damage = damageInfo1.GetDamage();
|
||||
|
||||
victim.DealDamageMods(this, ref damage);
|
||||
|
||||
SpellDamageShield damageShield = new();
|
||||
damageShield.Attacker = victim.GetGUID();
|
||||
damageShield.Defender = GetGUID();
|
||||
damageShield.SpellID = spellInfo.Id;
|
||||
damageShield.TotalDamage = damage;
|
||||
damageShield.OriginalDamage = (int)damageInfo.originalDamage;
|
||||
damageShield.OverKill = (uint)Math.Max(damage - GetHealth(), 0);
|
||||
damageShield.SchoolMask = (uint)spellInfo.SchoolMask;
|
||||
damageShield.LogAbsorbed = damageInfo1.GetAbsorb();
|
||||
|
||||
victim.DealDamage(this, damage, null, DamageEffectType.SpellDirect, spellInfo.GetSchoolMask(), spellInfo, true);
|
||||
damageShield.LogData.Initialize(this);
|
||||
|
||||
victim.SendCombatLogMessage(damageShield);
|
||||
}
|
||||
}
|
||||
}
|
||||
public uint DealDamage(Unit victim, uint damage, CleanDamage cleanDamage = null, DamageEffectType damagetype = DamageEffectType.Direct, SpellSchoolMask damageSchoolMask = SpellSchoolMask.Normal, SpellInfo spellProto = null, bool durabilityLoss = true)
|
||||
public static uint DealDamage(Unit attacker, Unit victim, uint damage, CleanDamage cleanDamage = null, DamageEffectType damagetype = DamageEffectType.Direct, SpellSchoolMask damageSchoolMask = SpellSchoolMask.Normal, SpellInfo spellProto = null, bool durabilityLoss = true)
|
||||
{
|
||||
if (victim.IsAIEnabled)
|
||||
victim.GetAI().DamageTaken(this, ref damage);
|
||||
victim.GetAI().DamageTaken(attacker, ref damage);
|
||||
|
||||
if (IsAIEnabled)
|
||||
GetAI().DamageDealt(victim, ref damage, damagetype);
|
||||
if (attacker != null && attacker.IsAIEnabled)
|
||||
attacker.GetAI().DamageDealt(victim, ref damage, damagetype);
|
||||
|
||||
// Hook for OnDamage Event
|
||||
Global.ScriptMgr.OnDamage(this, victim, ref damage);
|
||||
Global.ScriptMgr.OnDamage(attacker, victim, ref damage);
|
||||
|
||||
if (victim.IsTypeId(TypeId.Player) && this != victim)
|
||||
if (victim.IsTypeId(TypeId.Player) && attacker != victim)
|
||||
{
|
||||
// Signal to pets that their owner was attacked - except when DOT.
|
||||
if (damagetype != DamageEffectType.DOT)
|
||||
@@ -2719,7 +2588,7 @@ namespace Game.Entities
|
||||
Creature cControlled = controlled.ToCreature();
|
||||
if (cControlled != null)
|
||||
if (cControlled.IsAIEnabled)
|
||||
cControlled.GetAI().OwnerAttackedBy(this);
|
||||
cControlled.GetAI().OwnerAttackedBy(attacker);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2740,7 +2609,7 @@ namespace Game.Entities
|
||||
|
||||
if (damage == 0 && damagetype != DamageEffectType.DOT && cleanDamage != null && cleanDamage.absorbed_damage != 0)
|
||||
{
|
||||
if (victim != this && victim.IsPlayer())
|
||||
if (victim != attacker && victim.IsPlayer())
|
||||
{
|
||||
Spell spell = victim.GetCurrentSpell(CurrentSpellTypes.Generic);
|
||||
if (spell != null)
|
||||
@@ -2772,35 +2641,36 @@ namespace Game.Entities
|
||||
uint share = MathFunctions.CalculatePct(damage, aura.GetAmount());
|
||||
|
||||
// @todo check packets if damage is done by victim, or by attacker of victim
|
||||
DealDamageMods(shareDamageTarget, ref share);
|
||||
DealDamage(shareDamageTarget, share, null, DamageEffectType.NoDamage, spell.GetSchoolMask(), spell, false);
|
||||
DealDamageMods(attacker, shareDamageTarget, ref share);
|
||||
DealDamage(attacker, shareDamageTarget, share, null, DamageEffectType.NoDamage, spell.GetSchoolMask(), spell, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Rage from Damage made (only from direct weapon damage)
|
||||
if (cleanDamage != null && (cleanDamage.attackType == WeaponAttackType.BaseAttack || cleanDamage.attackType == WeaponAttackType.OffAttack) && damagetype == DamageEffectType.Direct && this != victim && GetPowerType() == PowerType.Rage)
|
||||
if (attacker != null && cleanDamage != null && (cleanDamage.attackType == WeaponAttackType.BaseAttack || cleanDamage.attackType == WeaponAttackType.OffAttack) && damagetype == DamageEffectType.Direct && attacker != victim && attacker.GetPowerType() == PowerType.Rage)
|
||||
{
|
||||
uint rage = (uint)(GetBaseAttackTime(cleanDamage.attackType) / 1000.0f * 1.75f);
|
||||
uint rage = (uint)(attacker.GetBaseAttackTime(cleanDamage.attackType) / 1000.0f * 1.75f);
|
||||
if (cleanDamage.attackType == WeaponAttackType.OffAttack)
|
||||
rage /= 2;
|
||||
RewardRage(rage);
|
||||
|
||||
attacker.RewardRage(rage);
|
||||
}
|
||||
|
||||
if (damage == 0)
|
||||
return 0;
|
||||
|
||||
Log.outDebug(LogFilter.Unit, "DealDamageStart");
|
||||
|
||||
uint health = (uint)victim.GetHealth();
|
||||
Log.outDebug(LogFilter.Unit, "Unit {0} dealt {1} damage to unit {2}", GetGUID(), damage, victim.GetGUID());
|
||||
|
||||
// duel ends when player has 1 or less hp
|
||||
bool duel_hasEnded = false;
|
||||
bool duel_wasMounted = false;
|
||||
if (victim.IsTypeId(TypeId.Player) && victim.ToPlayer().duel != null && damage >= (health - 1))
|
||||
if (victim.IsPlayer() && victim.ToPlayer().duel != null && damage >= (health - 1))
|
||||
{
|
||||
if (!attacker)
|
||||
return 0;
|
||||
|
||||
// prevent kill only if killed in duel and killed by opponent or opponent controlled creature
|
||||
if (victim.ToPlayer().duel.opponent == this || victim.ToPlayer().duel.opponent.GetGUID() == GetOwnerGUID())
|
||||
if (victim.ToPlayer().duel.opponent == attacker || victim.ToPlayer().duel.opponent.GetGUID() == attacker.GetOwnerGUID())
|
||||
damage = health - 1;
|
||||
|
||||
duel_hasEnded = true;
|
||||
@@ -2810,8 +2680,11 @@ namespace Game.Entities
|
||||
Player victimRider = victim.GetCharmer().ToPlayer();
|
||||
if (victimRider != null && victimRider.duel != null && victimRider.duel.isMounted)
|
||||
{
|
||||
if (!attacker)
|
||||
return 0;
|
||||
|
||||
// prevent kill only if killed in duel and killed by opponent or opponent controlled creature
|
||||
if (victimRider.duel.opponent == this || victimRider.duel.opponent.GetGUID() == GetCharmerGUID())
|
||||
if (victimRider.duel.opponent == attacker || victimRider.duel.opponent.GetGUID() == attacker.GetCharmerGUID())
|
||||
damage = health - 1;
|
||||
|
||||
duel_wasMounted = true;
|
||||
@@ -2819,35 +2692,36 @@ namespace Game.Entities
|
||||
}
|
||||
}
|
||||
|
||||
if (IsTypeId(TypeId.Player) && this != victim)
|
||||
if (attacker != null && attacker != victim)
|
||||
{
|
||||
Player killer = ToPlayer();
|
||||
|
||||
// in bg, count dmg if victim is also a player
|
||||
if (victim.IsTypeId(TypeId.Player))
|
||||
Player killer = attacker.ToPlayer();
|
||||
if (killer != null)
|
||||
{
|
||||
Battleground bg = killer.GetBattleground();
|
||||
if (bg)
|
||||
bg.UpdatePlayerScore(killer, ScoreType.DamageDone, damage);
|
||||
// in bg, count dmg if victim is also a player
|
||||
if (victim.IsPlayer())
|
||||
{
|
||||
Battleground bg = killer.GetBattleground();
|
||||
if (bg != null)
|
||||
bg.UpdatePlayerScore(killer, ScoreType.DamageDone, damage);
|
||||
}
|
||||
|
||||
killer.UpdateCriteria(CriteriaTypes.DamageDone, health > damage ? damage : health, 0, 0, victim);
|
||||
killer.UpdateCriteria(CriteriaTypes.HighestHitDealt, damage);
|
||||
}
|
||||
|
||||
killer.UpdateCriteria(CriteriaTypes.DamageDone, health > damage ? damage : health, 0, 0, victim);
|
||||
killer.UpdateCriteria(CriteriaTypes.HighestHitDealt, damage);
|
||||
}
|
||||
|
||||
if (victim.IsTypeId(TypeId.Player))
|
||||
{
|
||||
if (victim.IsPlayer())
|
||||
victim.ToPlayer().UpdateCriteria(CriteriaTypes.HighestHitReceived, damage);
|
||||
}
|
||||
|
||||
damage /= (uint)victim.GetHealthMultiplierForTarget(this);
|
||||
if (attacker != null)
|
||||
damage /= (uint)victim.GetHealthMultiplierForTarget(attacker);
|
||||
|
||||
if (victim.GetTypeId() != TypeId.Player && (!victim.IsControlledByPlayer() || victim.IsVehicle()))
|
||||
{
|
||||
if (!victim.ToCreature().HasLootRecipient())
|
||||
victim.ToCreature().SetLootRecipient(this);
|
||||
victim.ToCreature().SetLootRecipient(attacker);
|
||||
|
||||
if (IsControlledByPlayer())
|
||||
if (attacker == null || attacker.IsControlledByPlayer())
|
||||
victim.ToCreature().LowerPlayerDamageReq(health < damage ? health : damage);
|
||||
}
|
||||
|
||||
@@ -2858,15 +2732,13 @@ namespace Game.Entities
|
||||
{
|
||||
killed = true;
|
||||
|
||||
Log.outDebug(LogFilter.Unit, "DealDamage: victim just died");
|
||||
|
||||
if (victim.IsTypeId(TypeId.Player) && victim != this)
|
||||
if (victim.IsPlayer() && victim != attacker)
|
||||
victim.ToPlayer().UpdateCriteria(CriteriaTypes.TotalDamageReceived, health);
|
||||
|
||||
if (damagetype != DamageEffectType.NoDamage && damagetype != DamageEffectType.Self && victim.HasAuraType(AuraType.SchoolAbsorbOverkill))
|
||||
{
|
||||
var vAbsorbOverkill = victim.GetAuraEffectsByType(AuraType.SchoolAbsorbOverkill);
|
||||
DamageInfo damageInfo = new(this, victim, damage, spellProto, damageSchoolMask, damagetype, cleanDamage != null ? cleanDamage.attackType : WeaponAttackType.BaseAttack);
|
||||
DamageInfo damageInfo = new(attacker, victim, damage, spellProto, damageSchoolMask, damagetype, cleanDamage != null ? cleanDamage.attackType : WeaponAttackType.BaseAttack);
|
||||
|
||||
foreach (var absorbAurEff in vAbsorbOverkill)
|
||||
{
|
||||
@@ -2909,7 +2781,7 @@ namespace Game.Entities
|
||||
if (currentAbsorb != 0)
|
||||
{
|
||||
SpellAbsorbLog absorbLog = new();
|
||||
absorbLog.Attacker = GetGUID();
|
||||
absorbLog.Attacker = attacker != null ? attacker.GetGUID() : ObjectGuid.Empty;
|
||||
absorbLog.Victim = victim.GetGUID();
|
||||
absorbLog.Caster = baseAura.GetCasterGUID();
|
||||
absorbLog.AbsorbedSpellID = spellProto != null ? spellProto.Id : 0;
|
||||
@@ -2917,7 +2789,7 @@ namespace Game.Entities
|
||||
absorbLog.Absorbed = currentAbsorb;
|
||||
absorbLog.OriginalDamage = damageInfo.GetOriginalDamage();
|
||||
absorbLog.LogData.Initialize(victim);
|
||||
SendCombatLogMessage(absorbLog);
|
||||
victim.SendCombatLogMessage(absorbLog);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2926,11 +2798,9 @@ namespace Game.Entities
|
||||
}
|
||||
|
||||
if (killed)
|
||||
Kill(victim, durabilityLoss, skipSettingDeathState);
|
||||
Kill(attacker, victim, durabilityLoss, skipSettingDeathState);
|
||||
else
|
||||
{
|
||||
Log.outDebug(LogFilter.Unit, "DealDamageAlive");
|
||||
|
||||
if (victim.IsTypeId(TypeId.Player))
|
||||
victim.ToPlayer().UpdateCriteria(CriteriaTypes.TotalDamageReceived, damage);
|
||||
|
||||
@@ -2945,7 +2815,8 @@ namespace Game.Entities
|
||||
if (damagetype != DamageEffectType.DOT && damage > 0 && !victim.GetOwnerGUID().IsPlayer() && (spellProto == null || !spellProto.HasAura(AuraType.DamageShield)))
|
||||
victim.ToCreature().SetLastDamagedTime(GameTime.GetGameTime() + SharedConst.MaxAggroResetTime);
|
||||
|
||||
victim.GetThreatManager().AddThreat(this, damage, spellProto);
|
||||
if (attacker != null)
|
||||
victim.GetThreatManager().AddThreat(attacker, damage, spellProto);
|
||||
}
|
||||
else // victim is a player
|
||||
{
|
||||
@@ -2957,19 +2828,19 @@ namespace Game.Entities
|
||||
}
|
||||
}
|
||||
|
||||
if (IsTypeId(TypeId.Player))
|
||||
if (attacker != null && attacker.IsPlayer())
|
||||
{
|
||||
// random durability for items (HIT DONE)
|
||||
if (RandomHelper.randChance(WorldConfig.GetFloatValue(WorldCfg.RateDurabilityLossDamage)))
|
||||
{
|
||||
byte slot = (byte)RandomHelper.IRand(0, EquipmentSlot.End - 1);
|
||||
ToPlayer().DurabilityPointLossForEquipSlot(slot);
|
||||
attacker.ToPlayer().DurabilityPointLossForEquipSlot(slot);
|
||||
}
|
||||
}
|
||||
|
||||
if (damagetype != DamageEffectType.NoDamage)
|
||||
{
|
||||
if (victim != this && (spellProto == null || !(spellProto.HasAttribute(SpellAttr7.NoPushbackOnDamage) || spellProto.HasAttribute(SpellAttr3.TreatAsPeriodic))))
|
||||
if (victim != attacker && (spellProto == null || !(spellProto.HasAttribute(SpellAttr7.NoPushbackOnDamage) || spellProto.HasAttribute(SpellAttr3.TreatAsPeriodic))))
|
||||
{
|
||||
if (damagetype != DamageEffectType.DOT)
|
||||
{
|
||||
@@ -3044,11 +2915,147 @@ namespace Game.Entities
|
||||
}
|
||||
}
|
||||
|
||||
Log.outDebug(LogFilter.Unit, "DealDamageEnd returned {0} damage", damage);
|
||||
|
||||
return damage;
|
||||
}
|
||||
|
||||
void DealMeleeDamage(CalcDamageInfo damageInfo, bool durabilityLoss)
|
||||
{
|
||||
Unit victim = damageInfo.target;
|
||||
|
||||
if (!victim.IsAlive() || victim.HasUnitState(UnitState.InFlight) || (victim.IsTypeId(TypeId.Unit) && victim.ToCreature().IsEvadingAttacks()))
|
||||
return;
|
||||
|
||||
// Hmmmm dont like this emotes client must by self do all animations
|
||||
if (damageInfo.HitInfo.HasAnyFlag(HitInfo.CriticalHit))
|
||||
victim.HandleEmoteCommand(Emote.OneshotWoundCritical);
|
||||
if (damageInfo.blocked_amount != 0 && damageInfo.TargetState != VictimState.Blocks)
|
||||
victim.HandleEmoteCommand(Emote.OneshotParryShield);
|
||||
|
||||
if (damageInfo.TargetState == VictimState.Parry &&
|
||||
(!IsTypeId(TypeId.Unit) || !ToCreature().GetCreatureTemplate().FlagsExtra.HasAnyFlag(CreatureFlagsExtra.NoParryHasten)))
|
||||
{
|
||||
// Get attack timers
|
||||
float offtime = victim.GetAttackTimer(WeaponAttackType.OffAttack);
|
||||
float basetime = victim.GetAttackTimer(WeaponAttackType.BaseAttack);
|
||||
// Reduce attack time
|
||||
if (victim.HaveOffhandWeapon() && offtime < basetime)
|
||||
{
|
||||
float percent20 = victim.GetBaseAttackTime(WeaponAttackType.OffAttack) * 0.20f;
|
||||
float percent60 = 3.0f * percent20;
|
||||
if (offtime > percent20 && offtime <= percent60)
|
||||
victim.SetAttackTimer(WeaponAttackType.OffAttack, (uint)percent20);
|
||||
else if (offtime > percent60)
|
||||
{
|
||||
offtime -= 2.0f * percent20;
|
||||
victim.SetAttackTimer(WeaponAttackType.OffAttack, (uint)offtime);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float percent20 = victim.GetBaseAttackTime(WeaponAttackType.BaseAttack) * 0.20f;
|
||||
float percent60 = 3.0f * percent20;
|
||||
if (basetime > percent20 && basetime <= percent60)
|
||||
victim.SetAttackTimer(WeaponAttackType.BaseAttack, (uint)percent20);
|
||||
else if (basetime > percent60)
|
||||
{
|
||||
basetime -= 2.0f * percent20;
|
||||
victim.SetAttackTimer(WeaponAttackType.BaseAttack, (uint)basetime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call default DealDamage
|
||||
CleanDamage cleanDamage = new(damageInfo.cleanDamage, damageInfo.absorb, damageInfo.attackType, damageInfo.hitOutCome);
|
||||
DealDamage(this, victim, damageInfo.damage, cleanDamage, DamageEffectType.Direct, (SpellSchoolMask)damageInfo.damageSchoolMask, null, durabilityLoss);
|
||||
|
||||
// If this is a creature and it attacks from behind it has a probability to daze it's victim
|
||||
if ((damageInfo.hitOutCome == MeleeHitOutcome.Crit || damageInfo.hitOutCome == MeleeHitOutcome.Crushing || damageInfo.hitOutCome == MeleeHitOutcome.Normal || damageInfo.hitOutCome == MeleeHitOutcome.Glancing) &&
|
||||
!IsTypeId(TypeId.Player) && !ToCreature().IsControlledByPlayer() && !victim.HasInArc(MathFunctions.PI, this)
|
||||
&& (victim.IsTypeId(TypeId.Player) || !victim.ToCreature().IsWorldBoss()) && !victim.IsVehicle())
|
||||
{
|
||||
// 20% base chance
|
||||
float chance = 20.0f;
|
||||
|
||||
// there is a newbie protection, at level 10 just 7% base chance; assuming linear function
|
||||
if (victim.GetLevel() < 30)
|
||||
chance = 0.65f * victim.GetLevelForTarget(this) + 0.5f;
|
||||
|
||||
uint victimDefense = victim.GetMaxSkillValueForLevel(this);
|
||||
uint attackerMeleeSkill = GetMaxSkillValueForLevel();
|
||||
|
||||
chance *= attackerMeleeSkill / (float)victimDefense * 0.16f;
|
||||
|
||||
// -probability is between 0% and 40%
|
||||
MathFunctions.RoundToInterval(ref chance, 0.0f, 40.0f);
|
||||
|
||||
if (RandomHelper.randChance(chance))
|
||||
CastSpell(victim, 1604, true);
|
||||
}
|
||||
|
||||
if (IsTypeId(TypeId.Player))
|
||||
{
|
||||
DamageInfo dmgInfo = new(damageInfo);
|
||||
ToPlayer().CastItemCombatSpell(dmgInfo);
|
||||
}
|
||||
|
||||
// Do effect if any damage done to target
|
||||
if (damageInfo.damage != 0)
|
||||
{
|
||||
// We're going to call functions which can modify content of the list during iteration over it's elements
|
||||
// Let's copy the list so we can prevent iterator invalidation
|
||||
var vDamageShieldsCopy = victim.GetAuraEffectsByType(AuraType.DamageShield);
|
||||
foreach (var dmgShield in vDamageShieldsCopy)
|
||||
{
|
||||
SpellInfo spellInfo = dmgShield.GetSpellInfo();
|
||||
|
||||
// Damage shield can be resisted...
|
||||
var missInfo = victim.SpellHitResult(this, spellInfo, false);
|
||||
if (missInfo != SpellMissInfo.None)
|
||||
{
|
||||
victim.SendSpellMiss(this, spellInfo.Id, missInfo);
|
||||
continue;
|
||||
}
|
||||
|
||||
// ...or immuned
|
||||
if (IsImmunedToDamage(spellInfo))
|
||||
{
|
||||
victim.SendSpellDamageImmune(this, spellInfo.Id, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint damage = (uint)dmgShield.GetAmount();
|
||||
Unit caster = dmgShield.GetCaster();
|
||||
if (caster)
|
||||
{
|
||||
damage = caster.SpellDamageBonusDone(this, spellInfo, damage, DamageEffectType.SpellDirect, dmgShield.GetSpellEffectInfo());
|
||||
damage = SpellDamageBonusTaken(caster, spellInfo, damage, DamageEffectType.SpellDirect, dmgShield.GetSpellEffectInfo());
|
||||
}
|
||||
|
||||
DamageInfo damageInfo1 = new(this, victim, damage, spellInfo, spellInfo.GetSchoolMask(), DamageEffectType.SpellDirect, WeaponAttackType.BaseAttack);
|
||||
CalcAbsorbResist(damageInfo1);
|
||||
damage = damageInfo1.GetDamage();
|
||||
|
||||
DealDamageMods(victim, this, ref damage);
|
||||
|
||||
SpellDamageShield damageShield = new();
|
||||
damageShield.Attacker = victim.GetGUID();
|
||||
damageShield.Defender = GetGUID();
|
||||
damageShield.SpellID = spellInfo.Id;
|
||||
damageShield.TotalDamage = damage;
|
||||
damageShield.OriginalDamage = (int)damageInfo.originalDamage;
|
||||
damageShield.OverKill = (uint)Math.Max(damage - GetHealth(), 0);
|
||||
damageShield.SchoolMask = (uint)spellInfo.SchoolMask;
|
||||
damageShield.LogAbsorbed = damageInfo1.GetAbsorb();
|
||||
|
||||
DealDamage(victim, this, damage, null, DamageEffectType.SpellDirect, spellInfo.GetSchoolMask(), spellInfo, true);
|
||||
damageShield.LogData.Initialize(this);
|
||||
|
||||
victim.SendCombatLogMessage(damageShield);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public long ModifyHealth(long dVal)
|
||||
{
|
||||
long gain = 0;
|
||||
@@ -3338,7 +3345,7 @@ namespace Game.Entities
|
||||
RemovePvpFlag(UnitPVPStateFlags.PvP);
|
||||
}
|
||||
|
||||
uint CalcSpellResistedDamage(Unit attacker, Unit victim, uint damage, SpellSchoolMask schoolMask, SpellInfo spellInfo)
|
||||
static uint CalcSpellResistedDamage(Unit attacker, Unit victim, uint damage, SpellSchoolMask schoolMask, SpellInfo spellInfo)
|
||||
{
|
||||
// Magic damage, check for resists
|
||||
if (!Convert.ToBoolean(schoolMask & SpellSchoolMask.Magic))
|
||||
@@ -3359,7 +3366,7 @@ namespace Game.Entities
|
||||
return 0;
|
||||
}
|
||||
|
||||
float averageResist = CalculateAverageResistReduction(schoolMask, victim, spellInfo);
|
||||
float averageResist = CalculateAverageResistReduction(attacker, schoolMask, victim, spellInfo);
|
||||
|
||||
float[] discreteResistProbability = new float[11];
|
||||
if (averageResist <= 0.1f)
|
||||
@@ -3387,7 +3394,8 @@ namespace Game.Entities
|
||||
{
|
||||
int ignoredResistance = 0;
|
||||
|
||||
ignoredResistance += GetTotalAuraModifierByMiscMask(AuraType.ModIgnoreTargetResist, (int)schoolMask);
|
||||
if (attacker != null)
|
||||
ignoredResistance += attacker.GetTotalAuraModifierByMiscMask(AuraType.ModIgnoreTargetResist, (int)schoolMask);
|
||||
|
||||
ignoredResistance = Math.Min(ignoredResistance, 100);
|
||||
MathFunctions.ApplyPct(ref damageResisted, 100 - ignoredResistance);
|
||||
@@ -3407,20 +3415,23 @@ namespace Game.Entities
|
||||
return (uint)damageResisted;
|
||||
}
|
||||
|
||||
float CalculateAverageResistReduction(SpellSchoolMask schoolMask, Unit victim, SpellInfo spellInfo)
|
||||
static float CalculateAverageResistReduction(Unit attacker, SpellSchoolMask schoolMask, Unit victim, SpellInfo spellInfo = null)
|
||||
{
|
||||
float victimResistance = (float)victim.GetResistance(schoolMask);
|
||||
float victimResistance = victim.GetResistance(schoolMask);
|
||||
|
||||
// pets inherit 100% of masters penetration
|
||||
// excluding traps
|
||||
Player player = GetSpellModOwner();
|
||||
if (player != null && GetEntry() != SharedConst.WorldTrigger)
|
||||
if (attacker != null)
|
||||
{
|
||||
victimResistance += (float)player.GetTotalAuraModifierByMiscMask(AuraType.ModTargetResistance, (int)schoolMask);
|
||||
victimResistance -= (float)player.GetSpellPenetrationItemMod();
|
||||
// pets inherit 100% of masters penetration
|
||||
// excluding traps
|
||||
Player player = attacker.GetSpellModOwner();
|
||||
if (player != null && attacker.GetEntry() != SharedConst.WorldTrigger)
|
||||
{
|
||||
victimResistance += player.GetTotalAuraModifierByMiscMask(AuraType.ModTargetResistance, (int)schoolMask);
|
||||
victimResistance -= player.GetSpellPenetrationItemMod();
|
||||
}
|
||||
else
|
||||
victimResistance += attacker.GetTotalAuraModifierByMiscMask(AuraType.ModTargetResistance, (int)schoolMask);
|
||||
}
|
||||
else
|
||||
victimResistance += (float)GetTotalAuraModifierByMiscMask(AuraType.ModTargetResistance, (int)schoolMask);
|
||||
|
||||
// holy resistance exists in pve and comes from level difference, ignore template values
|
||||
if (schoolMask.HasAnyFlag(SpellSchoolMask.Holy))
|
||||
@@ -3433,12 +3444,12 @@ namespace Game.Entities
|
||||
victimResistance = Math.Max(victimResistance, 0.0f);
|
||||
|
||||
// level-based resistance does not apply to binary spells, and cannot be overcome by spell penetration
|
||||
if (spellInfo == null || !spellInfo.HasAttribute(SpellCustomAttributes.BinarySpell))
|
||||
victimResistance += Math.Max(((float)victim.GetLevelForTarget(this) - (float)GetLevelForTarget(victim)) * 5.0f, 0.0f);
|
||||
if (attacker != null && (spellInfo == null || !spellInfo.HasAttribute(SpellCustomAttributes.BinarySpell)))
|
||||
victimResistance += Math.Max(((float)victim.GetLevelForTarget(attacker) - (float)attacker.GetLevelForTarget(victim)) * 5.0f, 0.0f);
|
||||
|
||||
uint bossLevel = 83;
|
||||
float bossResistanceConstant = 510.0f;
|
||||
uint level = victim.GetLevelForTarget(this);
|
||||
uint level = attacker != null ? victim.GetLevelForTarget(attacker) : attacker.GetLevel();
|
||||
float resistanceConstant;
|
||||
|
||||
if (level == bossLevel)
|
||||
@@ -3449,7 +3460,7 @@ namespace Game.Entities
|
||||
return victimResistance / (victimResistance + resistanceConstant);
|
||||
}
|
||||
|
||||
public void CalcAbsorbResist(DamageInfo damageInfo)
|
||||
public static void CalcAbsorbResist(DamageInfo damageInfo)
|
||||
{
|
||||
if (!damageInfo.GetVictim() || !damageInfo.GetVictim().IsAlive() || damageInfo.GetDamage() == 0)
|
||||
return;
|
||||
@@ -3458,7 +3469,11 @@ namespace Game.Entities
|
||||
damageInfo.ResistDamage(resistedDamage);
|
||||
|
||||
// Ignore Absorption Auras
|
||||
float auraAbsorbMod = GetMaxPositiveAuraModifierByMiscMask(AuraType.ModTargetAbsorbSchool, (uint)damageInfo.GetSchoolMask());
|
||||
float auraAbsorbMod = 0f;
|
||||
|
||||
Unit attacker = damageInfo.GetAttacker();
|
||||
if (attacker != null)
|
||||
auraAbsorbMod = attacker.GetMaxPositiveAuraModifierByMiscMask(AuraType.ModTargetAbsorbSchool, (uint)damageInfo.GetSchoolMask());
|
||||
|
||||
MathFunctions.RoundToInterval(ref auraAbsorbMod, 0.0f, 100.0f);
|
||||
|
||||
@@ -3522,7 +3537,7 @@ namespace Game.Entities
|
||||
if (currentAbsorb != 0)
|
||||
{
|
||||
SpellAbsorbLog absorbLog = new();
|
||||
absorbLog.Attacker = damageInfo.GetAttacker().GetGUID();
|
||||
absorbLog.Attacker = damageInfo.GetAttacker() != null ? damageInfo.GetAttacker().GetGUID() : ObjectGuid.Empty;
|
||||
absorbLog.Victim = damageInfo.GetVictim().GetGUID();
|
||||
absorbLog.Caster = absorbAurEff.GetBase().GetCasterGUID();
|
||||
absorbLog.AbsorbedSpellID = damageInfo.GetSpellInfo() != null ? damageInfo.GetSpellInfo().Id : 0;
|
||||
@@ -3530,7 +3545,7 @@ namespace Game.Entities
|
||||
absorbLog.Absorbed = currentAbsorb;
|
||||
absorbLog.OriginalDamage = damageInfo.GetOriginalDamage();
|
||||
absorbLog.LogData.Initialize(damageInfo.GetVictim());
|
||||
SendCombatLogMessage(absorbLog);
|
||||
damageInfo.GetVictim().SendCombatLogMessage(absorbLog);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3596,7 +3611,7 @@ namespace Game.Entities
|
||||
if (currentAbsorb != 0)
|
||||
{
|
||||
SpellAbsorbLog absorbLog = new();
|
||||
absorbLog.Attacker = damageInfo.GetAttacker().GetGUID();
|
||||
absorbLog.Attacker = damageInfo.GetAttacker() != null ? damageInfo.GetAttacker().GetGUID() : ObjectGuid.Empty;
|
||||
absorbLog.Victim = damageInfo.GetVictim().GetGUID();
|
||||
absorbLog.Caster = absorbAurEff.GetBase().GetCasterGUID();
|
||||
absorbLog.AbsorbedSpellID = damageInfo.GetSpellInfo() != null ? damageInfo.GetSpellInfo().Id : 0;
|
||||
@@ -3604,14 +3619,14 @@ namespace Game.Entities
|
||||
absorbLog.Absorbed = currentAbsorb;
|
||||
absorbLog.OriginalDamage = damageInfo.GetOriginalDamage();
|
||||
absorbLog.LogData.Initialize(damageInfo.GetVictim());
|
||||
SendCombatLogMessage(absorbLog);
|
||||
damageInfo.GetVictim().SendCombatLogMessage(absorbLog);
|
||||
}
|
||||
}
|
||||
|
||||
damageInfo.ModifyDamage(absorbIgnoringDamage);
|
||||
|
||||
// split damage auras - only when not damaging self
|
||||
if (damageInfo.GetVictim() != this)
|
||||
if (damageInfo.GetVictim() != damageInfo.GetAttacker())
|
||||
{
|
||||
// We're going to call functions which can modify content of the list during iteration over it's elements
|
||||
// Let's copy the list so we can prevent iterator invalidation
|
||||
@@ -3652,23 +3667,23 @@ namespace Game.Entities
|
||||
}
|
||||
|
||||
uint split_absorb = 0;
|
||||
DealDamageMods(caster, ref splitDamage, ref split_absorb);
|
||||
DealDamageMods(damageInfo.GetAttacker(), caster, ref splitDamage, ref split_absorb);
|
||||
|
||||
SpellNonMeleeDamage log = new(this, caster, itr.GetSpellInfo(), itr.GetBase().GetSpellVisual(), damageInfo.GetSchoolMask(), itr.GetBase().GetCastGUID());
|
||||
SpellNonMeleeDamage log = new(damageInfo.GetAttacker(), caster, itr.GetSpellInfo(), itr.GetBase().GetSpellVisual(), damageInfo.GetSchoolMask(), itr.GetBase().GetCastGUID());
|
||||
CleanDamage cleanDamage = new(splitDamage, 0, WeaponAttackType.BaseAttack, MeleeHitOutcome.Normal);
|
||||
DealDamage(caster, splitDamage, cleanDamage, DamageEffectType.Direct, damageInfo.GetSchoolMask(), itr.GetSpellInfo(), false);
|
||||
DealDamage(damageInfo.GetAttacker(), caster, splitDamage, cleanDamage, DamageEffectType.Direct, damageInfo.GetSchoolMask(), itr.GetSpellInfo(), false);
|
||||
log.damage = splitDamage;
|
||||
log.originalDamage = splitDamage;
|
||||
log.absorb = split_absorb;
|
||||
SendSpellNonMeleeDamageLog(log);
|
||||
caster.SendSpellNonMeleeDamageLog(log);
|
||||
|
||||
// break 'Fear' and similar auras
|
||||
ProcSkillsAndAuras(caster, ProcFlags.None, ProcFlags.TakenSpellMagicDmgClassNeg, ProcFlagsSpellType.Damage, ProcFlagsSpellPhase.Hit, ProcFlagsHit.None, null, damageInfo, null);
|
||||
ProcSkillsAndAuras(damageInfo.GetAttacker(), caster, ProcFlags.None, ProcFlags.TakenSpellMagicDmgClassNeg, ProcFlagsSpellType.Damage, ProcFlagsSpellPhase.Hit, ProcFlagsHit.None, null, damageInfo, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CalcHealAbsorb(HealInfo healInfo)
|
||||
public static void CalcHealAbsorb(HealInfo healInfo)
|
||||
{
|
||||
if (healInfo.GetHeal() == 0)
|
||||
return;
|
||||
@@ -3728,61 +3743,67 @@ namespace Game.Entities
|
||||
}
|
||||
}
|
||||
|
||||
public uint CalcArmorReducedDamage(Unit attacker, Unit victim, uint damage, SpellInfo spellInfo, WeaponAttackType attackType = WeaponAttackType.Max)
|
||||
public static uint CalcArmorReducedDamage(Unit attacker, Unit victim, uint damage, SpellInfo spellInfo, WeaponAttackType attackType = WeaponAttackType.Max, uint attackerLevel = 0)
|
||||
{
|
||||
float armor = victim.GetArmor();
|
||||
|
||||
armor *= victim.GetArmorMultiplierForTarget(attacker);
|
||||
|
||||
// bypass enemy armor by SPELL_AURA_BYPASS_ARMOR_FOR_CASTER
|
||||
int armorBypassPct = 0;
|
||||
var reductionAuras = victim.GetAuraEffectsByType(AuraType.BypassArmorForCaster);
|
||||
foreach (var eff in reductionAuras)
|
||||
if (eff.GetCasterGUID() == GetGUID())
|
||||
armorBypassPct += eff.GetAmount();
|
||||
armor = MathFunctions.CalculatePct(armor, 100 - Math.Min(armorBypassPct, 100));
|
||||
|
||||
// Ignore enemy armor by SPELL_AURA_MOD_TARGET_RESISTANCE aura
|
||||
armor += GetTotalAuraModifierByMiscMask(AuraType.ModTargetResistance, (int)SpellSchoolMask.Normal);
|
||||
|
||||
if (spellInfo != null)
|
||||
if (attacker != null)
|
||||
{
|
||||
Player modOwner = GetSpellModOwner();
|
||||
if (modOwner != null)
|
||||
modOwner.ApplySpellMod(spellInfo, SpellModOp.TargetResistance, ref armor);
|
||||
}
|
||||
armor *= victim.GetArmorMultiplierForTarget(attacker);
|
||||
|
||||
var resIgnoreAuras = GetAuraEffectsByType(AuraType.ModIgnoreTargetResist);
|
||||
foreach (var eff in resIgnoreAuras)
|
||||
{
|
||||
if (eff.GetMiscValue().HasAnyFlag((int)SpellSchoolMask.Normal) && eff.IsAffectingSpell(spellInfo))
|
||||
armor = (float)Math.Floor(MathFunctions.AddPct(ref armor, -eff.GetAmount()));
|
||||
}
|
||||
// bypass enemy armor by SPELL_AURA_BYPASS_ARMOR_FOR_CASTER
|
||||
int armorBypassPct = 0;
|
||||
var reductionAuras = victim.GetAuraEffectsByType(AuraType.BypassArmorForCaster);
|
||||
foreach (var eff in reductionAuras)
|
||||
if (eff.GetCasterGUID() == attacker.GetGUID())
|
||||
armorBypassPct += eff.GetAmount();
|
||||
|
||||
// Apply Player CR_ARMOR_PENETRATION rating
|
||||
if (IsTypeId(TypeId.Player))
|
||||
{
|
||||
float arpPct = ToPlayer().GetRatingBonusValue(CombatRating.ArmorPenetration);
|
||||
armor = MathFunctions.CalculatePct(armor, 100 - Math.Min(armorBypassPct, 100));
|
||||
|
||||
// no more than 100%
|
||||
MathFunctions.RoundToInterval(ref arpPct, 0.0f, 100.0f);
|
||||
// Ignore enemy armor by SPELL_AURA_MOD_TARGET_RESISTANCE aura
|
||||
armor += attacker.GetTotalAuraModifierByMiscMask(AuraType.ModTargetResistance, (int)SpellSchoolMask.Normal);
|
||||
|
||||
float maxArmorPen;
|
||||
if (victim.GetLevelForTarget(attacker) < 60)
|
||||
maxArmorPen = 400 + 85 * victim.GetLevelForTarget(attacker);
|
||||
else
|
||||
maxArmorPen = 400 + 85 * victim.GetLevelForTarget(attacker) + 4.5f * 85 * (victim.GetLevelForTarget(attacker) - 59);
|
||||
if (spellInfo != null)
|
||||
{
|
||||
Player modOwner = attacker.GetSpellModOwner();
|
||||
if (modOwner != null)
|
||||
modOwner.ApplySpellMod(spellInfo, SpellModOp.TargetResistance, ref armor);
|
||||
}
|
||||
|
||||
// Cap armor penetration to this number
|
||||
maxArmorPen = Math.Min((armor + maxArmorPen) / 3.0f, armor);
|
||||
// Figure out how much armor do we ignore
|
||||
armor -= MathFunctions.CalculatePct(maxArmorPen, arpPct);
|
||||
var resIgnoreAuras = attacker.GetAuraEffectsByType(AuraType.ModIgnoreTargetResist);
|
||||
foreach (var eff in resIgnoreAuras)
|
||||
{
|
||||
if (eff.GetMiscValue().HasAnyFlag((int)SpellSchoolMask.Normal) && eff.IsAffectingSpell(spellInfo))
|
||||
armor = (float)Math.Floor(MathFunctions.AddPct(ref armor, -eff.GetAmount()));
|
||||
}
|
||||
|
||||
// Apply Player CR_ARMOR_PENETRATION rating
|
||||
if (attacker.IsPlayer())
|
||||
{
|
||||
float arpPct = attacker.ToPlayer().GetRatingBonusValue(CombatRating.ArmorPenetration);
|
||||
|
||||
// no more than 100%
|
||||
MathFunctions.RoundToInterval(ref arpPct, 0.0f, 100.0f);
|
||||
|
||||
float maxArmorPen;
|
||||
if (victim.GetLevelForTarget(attacker) < 60)
|
||||
maxArmorPen = 400 + 85 * victim.GetLevelForTarget(attacker);
|
||||
else
|
||||
maxArmorPen = 400 + 85 * victim.GetLevelForTarget(attacker) + 4.5f * 85 * (victim.GetLevelForTarget(attacker) - 59);
|
||||
|
||||
// Cap armor penetration to this number
|
||||
maxArmorPen = Math.Min((armor + maxArmorPen) / 3.0f, armor);
|
||||
// Figure out how much armor do we ignore
|
||||
armor -= MathFunctions.CalculatePct(maxArmorPen, arpPct);
|
||||
}
|
||||
}
|
||||
|
||||
if (MathFunctions.fuzzyLe(armor, 0.0f))
|
||||
return damage;
|
||||
|
||||
uint attackerLevel = attacker.GetLevelForTarget(victim);
|
||||
if (attacker != null)
|
||||
attackerLevel = attacker.GetLevelForTarget(victim);
|
||||
|
||||
// Expansion and ContentTuningID necessary? Does Player get a ContentTuningID too ?
|
||||
float armorConstant = Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.ArmorConstant, attackerLevel, -2, 0, attacker.GetClass());
|
||||
if ((armor + armorConstant) == 0)
|
||||
|
||||
@@ -207,7 +207,7 @@ namespace Game
|
||||
if (opcode == ClientOpcodes.MoveJump)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,7 +367,7 @@ namespace Game.Maps
|
||||
var playerList = instance.GetPlayers();
|
||||
foreach (var player in playerList)
|
||||
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;
|
||||
}
|
||||
case EncounterState.Fail:
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
if (m_owner.GetGUID() == m_casterGuid)
|
||||
@@ -854,13 +867,8 @@ namespace Game.Spells
|
||||
effect.ChangeAmount(effect.CalculateAmount(caster), false, true);
|
||||
|
||||
foreach (var app in applications)
|
||||
{
|
||||
if (!app.HasRemoveMode())
|
||||
{
|
||||
HandleAuraSpecificPeriodics(app, caster);
|
||||
HandleAuraSpecificMods(app, caster, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// unit not in world or during remove from world
|
||||
@@ -1842,7 +1801,7 @@ namespace Game.Spells
|
||||
public float CalcPPMProcChance(Unit actor)
|
||||
{
|
||||
// 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;
|
||||
|
||||
var currentTime = GameTime.GetGameTimeSteadyPoint();
|
||||
|
||||
@@ -39,12 +39,11 @@ namespace Game.Spells
|
||||
m_spellInfo = baseAura.GetSpellInfo();
|
||||
_effectInfo = spellEfffectInfo;
|
||||
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_isPeriodic = false;
|
||||
|
||||
CalculatePeriodic(caster, true, false);
|
||||
m_amount = CalculateAmount(caster);
|
||||
_amount = CalculateAmount(caster);
|
||||
CalculateSpellMod();
|
||||
}
|
||||
|
||||
@@ -267,7 +266,7 @@ namespace Game.Spells
|
||||
if (Convert.ToBoolean(handleMask & AuraEffectHandleModes.ChangeAmount))
|
||||
{
|
||||
if (!mark)
|
||||
m_amount = newAmount;
|
||||
_amount = newAmount;
|
||||
else
|
||||
SetAmount(newAmount);
|
||||
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)
|
||||
{
|
||||
if (spell == null)
|
||||
@@ -766,9 +770,9 @@ namespace Game.Spells
|
||||
public int GetMiscValueB() { return GetSpellEffectInfo().MiscValueB; }
|
||||
public int GetMiscValue() { return GetSpellEffectInfo().MiscValue; }
|
||||
public AuraType GetAuraType() { return GetSpellEffectInfo().ApplyAuraName; }
|
||||
public int GetAmount() { return m_amount; }
|
||||
public bool HasAmount() { return m_amount != 0; }
|
||||
public void SetAmount(int _amount) { m_amount = _amount; m_canBeRecalculated = false; }
|
||||
public int GetAmount() { return _amount; }
|
||||
public bool HasAmount() { return _amount != 0; }
|
||||
public void SetAmount(int amount) { _amount = amount; m_canBeRecalculated = false; }
|
||||
|
||||
public int GetPeriodicTimer() { return _periodicTimer; }
|
||||
public void SetPeriodicTimer(int periodicTimer) { _periodicTimer = periodicTimer; }
|
||||
@@ -791,13 +795,6 @@ namespace Game.Spells
|
||||
public bool CanBeRecalculated() { return m_canBeRecalculated; }
|
||||
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 uint GetTickNumber() { return _ticksDone; }
|
||||
public uint GetRemainingTicks() { return GetTotalTicks() - _ticksDone; }
|
||||
@@ -822,10 +819,7 @@ namespace Game.Spells
|
||||
SpellModifier m_spellmod;
|
||||
|
||||
public int m_baseAmount;
|
||||
int m_amount;
|
||||
int m_damage;
|
||||
float m_critChance;
|
||||
float m_donePct;
|
||||
int _amount;
|
||||
|
||||
// periodic stuff
|
||||
int _periodicTimer;
|
||||
@@ -2518,7 +2512,7 @@ namespace Game.Spells
|
||||
// so this break such spells or most of them.
|
||||
// Current formula about m_amount: effect base points + dieside - 1
|
||||
// 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
|
||||
{
|
||||
@@ -2527,7 +2521,7 @@ namespace Game.Spells
|
||||
|
||||
if (GetId() == 53111) // Devour Humanoid
|
||||
{
|
||||
target.Kill(caster);
|
||||
Unit.Kill(target, caster);
|
||||
if (caster.IsTypeId(TypeId.Unit))
|
||||
caster.ToCreature().DespawnOrUnsummon();
|
||||
}
|
||||
@@ -2985,7 +2979,7 @@ namespace Game.Spells
|
||||
if (GetMiscValue() == i || GetMiscValue() == -1)
|
||||
{
|
||||
if (apply)
|
||||
target.ApplyStatPctModifier(UnitMods.StatStart + i, UnitModifierPctType.Base, m_amount);
|
||||
target.ApplyStatPctModifier(UnitMods.StatStart + i, UnitModifierPctType.Base, GetAmount());
|
||||
else
|
||||
{
|
||||
float amount = target.GetTotalAuraMultiplier(AuraType.ModPercentStat, aurEff =>
|
||||
@@ -3154,7 +3148,7 @@ namespace Game.Spells
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -3170,7 +3164,7 @@ namespace Game.Spells
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
target.ApplyModOverrideSpellPowerByAPPercent(m_amount, apply);
|
||||
target.ApplyModOverrideSpellPowerByAPPercent(GetAmount(), apply);
|
||||
target.UpdateSpellDamageAndHealingBonus();
|
||||
}
|
||||
|
||||
@@ -3184,7 +3178,7 @@ namespace Game.Spells
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
target.ApplyModOverrideAPBySpellPowerPercent(m_amount, apply);
|
||||
target.ApplyModOverrideAPBySpellPowerPercent(GetAmount(), apply);
|
||||
target.UpdateAttackPowerAndDamage();
|
||||
target.UpdateAttackPowerAndDamage(true);
|
||||
}
|
||||
@@ -3730,7 +3724,7 @@ namespace Game.Spells
|
||||
target.ApplyAttackTimePercentMod(WeaponAttackType.RangedAttack, spellGroupVal, !apply);
|
||||
}
|
||||
|
||||
target.ApplyCastTimePercentMod(m_amount, apply);
|
||||
target.ApplyCastTimePercentMod(GetAmount(), apply);
|
||||
target.ApplyAttackTimePercentMod(WeaponAttackType.BaseAttack, GetAmount(), apply);
|
||||
target.ApplyAttackTimePercentMod(WeaponAttackType.OffAttack, GetAmount(), apply);
|
||||
target.ApplyAttackTimePercentMod(WeaponAttackType.RangedAttack, GetAmount(), apply);
|
||||
@@ -4443,7 +4437,7 @@ namespace Game.Spells
|
||||
|
||||
//Adding items
|
||||
uint noSpaceForCount;
|
||||
uint count = (uint)m_amount;
|
||||
uint count = (uint)GetAmount();
|
||||
|
||||
List<ItemPosCount> dest = new();
|
||||
InventoryResult msg = plCaster.CanStoreNewItem(ItemConst.NullBag, ItemConst.NullSlot, dest, GetSpellEffectInfo().ItemType, count, out noSpaceForCount);
|
||||
@@ -4493,7 +4487,7 @@ namespace Game.Spells
|
||||
return;
|
||||
|
||||
uint factionId = (uint)GetMiscValue();
|
||||
ReputationRank factionRank = (ReputationRank)m_amount;
|
||||
ReputationRank factionRank = (ReputationRank)GetAmount();
|
||||
|
||||
player.GetReputationMgr().ApplyForceReaction(factionId, factionRank, apply);
|
||||
player.GetReputationMgr().SendForceReactions();
|
||||
@@ -4880,7 +4874,10 @@ namespace Game.Spells
|
||||
|
||||
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;
|
||||
|
||||
if (target.HasUnitState(UnitState.Isolated) || target.IsImmunedToDamage(GetSpellInfo()))
|
||||
@@ -4896,38 +4893,28 @@ namespace Game.Spells
|
||||
|
||||
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
|
||||
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
|
||||
if (isAreaAura)
|
||||
Global.ScriptMgr.ModifyPeriodicDamageAurasTick(target, caster, ref damage);
|
||||
Global.ScriptMgr.ModifyPeriodicDamageAurasTick(target, caster, ref damage);
|
||||
|
||||
switch (GetAuraType())
|
||||
{
|
||||
case AuraType.PeriodicDamage:
|
||||
{
|
||||
if (isAreaAura)
|
||||
damage = (uint)(caster.SpellDamageBonusDone(target, GetSpellInfo(), damage, DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount()) * caster.SpellDamagePctDone(target, m_spellInfo, DamageEffectType.DOT));
|
||||
if (caster != null)
|
||||
damage = (uint)caster.SpellDamageBonusDone(target, 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
|
||||
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))
|
||||
caster.CastSpell(caster, 95810, new CastSpellExtraArgs(this));
|
||||
}
|
||||
|
||||
if (GetSpellInfo().SpellFamilyName == SpellFamilyNames.Generic)
|
||||
else if (GetSpellInfo().SpellFamilyName == SpellFamilyNames.Generic)
|
||||
{
|
||||
switch (GetId())
|
||||
{
|
||||
@@ -4947,48 +4934,54 @@ namespace Game.Spells
|
||||
{
|
||||
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)
|
||||
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;
|
||||
}
|
||||
case AuraType.PeriodicDamagePercent:
|
||||
// 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 = target.SpellDamageBonusTaken(caster, GetSpellInfo(), damage, DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount());
|
||||
break;
|
||||
default:
|
||||
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)
|
||||
{
|
||||
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));
|
||||
}
|
||||
uint damageReductedArmor = Unit.CalcArmorReducedDamage(caster, target, damage, GetSpellInfo(), GetSpellInfo().GetAttackType(), GetBase().GetCasterLevel());
|
||||
cleanDamage.mitigated_damage += damage - damageReductedArmor;
|
||||
damage = damageReductedArmor;
|
||||
}
|
||||
|
||||
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))
|
||||
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);
|
||||
caster.CalcAbsorbResist(damageInfo);
|
||||
Unit.CalcAbsorbResist(damageInfo);
|
||||
damage = damageInfo.GetDamage();
|
||||
|
||||
uint absorb = damageInfo.GetAbsorb();
|
||||
uint resist = damageInfo.GetResist();
|
||||
caster.DealDamageMods(target, ref damage, ref absorb);
|
||||
Unit.DealDamageMods(caster, target, ref damage, ref absorb);
|
||||
|
||||
// Set trigger flag
|
||||
ProcFlags procAttacker = ProcFlags.DonePeriodic;
|
||||
@@ -5005,17 +4998,20 @@ namespace Game.Spells
|
||||
if (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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (target.HasUnitState(UnitState.Isolated) || target.IsImmunedToDamage(GetSpellInfo()))
|
||||
@@ -5030,47 +5026,40 @@ namespace Game.Spells
|
||||
|
||||
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
|
||||
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());
|
||||
|
||||
bool crit = RandomHelper.randChance(GetCritChanceFor(caster, target));
|
||||
if (crit)
|
||||
damage = Unit.SpellCriticalDamageBonus(caster, m_spellInfo, damage, target);
|
||||
|
||||
// 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;
|
||||
damage = damageReductedArmor;
|
||||
}
|
||||
|
||||
if (!m_spellInfo.HasAttribute(SpellAttr4.FixedDamage))
|
||||
if (!GetSpellInfo().HasAttribute(SpellAttr4.FixedDamage))
|
||||
{
|
||||
if (GetSpellEffectInfo().IsTargetingArea() || isAreaAura)
|
||||
{
|
||||
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));
|
||||
}
|
||||
if (GetSpellEffectInfo().IsTargetingArea() || GetSpellEffectInfo().IsAreaAuraEffect() || GetSpellEffectInfo().Effect == SpellEffectName.PersistentAreaAura)
|
||||
damage = (uint)target.CalculateAOEAvoidance((int)damage, (uint)m_spellInfo.SchoolMask, caster);
|
||||
}
|
||||
|
||||
bool crit = RandomHelper.randChance(isAreaAura ? caster.GetUnitSpellCriticalChance(target, null, this, m_spellInfo.GetSchoolMask()) : m_critChance);
|
||||
if (crit)
|
||||
damage = caster.SpellCriticalDamageBonus(m_spellInfo, damage, target);
|
||||
int dmg = (int)damage;
|
||||
if (!GetSpellInfo().HasAttribute(SpellAttr4.FixedDamage) && caster != null && caster.CanApplyResilience())
|
||||
Unit.ApplyResilience(target, ref dmg);
|
||||
|
||||
uint dmg = damage;
|
||||
if (!GetSpellInfo().HasAttribute(SpellAttr4.FixedDamage))
|
||||
caster.ApplyResilience(target, ref dmg);
|
||||
damage = dmg;
|
||||
damage = (uint)dmg;
|
||||
|
||||
DamageInfo damageInfo = new(caster, target, damage, GetSpellInfo(), GetSpellInfo().GetSchoolMask(), DamageEffectType.DOT, WeaponAttackType.BaseAttack);
|
||||
caster.CalcAbsorbResist(damageInfo);
|
||||
Unit.CalcAbsorbResist(damageInfo);
|
||||
|
||||
uint absorb = damageInfo.GetAbsorb();
|
||||
uint resist = damageInfo.GetResist();
|
||||
@@ -5078,7 +5067,7 @@ namespace Game.Spells
|
||||
// SendSpellNonMeleeDamageLog expects non-absorbed/non-resisted damage
|
||||
SpellNonMeleeDamage log = new(caster, target, GetSpellInfo(), GetBase().GetSpellVisual(), GetSpellInfo().GetSchoolMask(), GetBase().GetCastGUID());
|
||||
log.damage = damage;
|
||||
log.originalDamage = dmg;
|
||||
log.originalDamage = (uint)dmg;
|
||||
log.absorb = absorb;
|
||||
log.resist = resist;
|
||||
log.periodicLog = true;
|
||||
@@ -5096,22 +5085,22 @@ namespace Game.Spells
|
||||
procVictim |= ProcFlags.TakenDamage;
|
||||
}
|
||||
|
||||
int new_damage = (int)caster.DealDamage(target, damage, cleanDamage, DamageEffectType.DOT, GetSpellInfo().GetSchoolMask(), GetSpellInfo(), false);
|
||||
if (caster.IsAlive())
|
||||
{
|
||||
caster.ProcSkillsAndAuras(target, procAttacker, procVictim, ProcFlagsSpellType.Damage, ProcFlagsSpellPhase.None, hitMask, null, damageInfo, null);
|
||||
int new_damage = (int)Unit.DealDamage(caster, target, damage, cleanDamage, DamageEffectType.DOT, GetSpellInfo().GetSchoolMask(), GetSpellInfo(), false);
|
||||
if (!caster || !caster.IsAlive())
|
||||
return;
|
||||
|
||||
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()));
|
||||
heal = (caster.SpellHealingBonusTaken(caster, GetSpellInfo(), heal, DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount()));
|
||||
float gainMultiplier = GetSpellEffectInfo().CalcValueMultiplier(caster);
|
||||
|
||||
HealInfo healInfo = new(caster, caster, heal, GetSpellInfo(), GetSpellInfo().GetSchoolMask());
|
||||
caster.HealBySpell(healInfo);
|
||||
uint heal = (caster.SpellHealingBonusDone(caster, GetSpellInfo(), (uint)(new_damage * gainMultiplier), DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount()));
|
||||
heal = caster.SpellHealingBonusTaken(caster, GetSpellInfo(), heal, DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount());
|
||||
|
||||
caster.GetThreatManager().ForwardThreatForAssistingMe(caster, healInfo.GetEffectiveHeal() * 0.5f, GetSpellInfo());
|
||||
caster.ProcSkillsAndAuras(caster, ProcFlags.DonePeriodic, ProcFlags.TakenPeriodic, ProcFlagsSpellType.Heal, ProcFlagsSpellPhase.None, hitMask, null, null, healInfo);
|
||||
}
|
||||
HealInfo healInfo = new(caster, caster, heal, GetSpellInfo(), GetSpellInfo().GetSchoolMask());
|
||||
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);
|
||||
}
|
||||
@@ -5143,12 +5132,15 @@ namespace Game.Spells
|
||||
|
||||
HealInfo healInfo = new(caster, target, damage, GetSpellInfo(), GetSpellInfo().GetSchoolMask());
|
||||
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)
|
||||
{
|
||||
if (!caster || !target.IsAlive())
|
||||
// dynobj auras must always have a caster
|
||||
Cypher.Assert(GetSpellEffectInfo().Effect != SpellEffectName.PersistentAreaAura || caster != null);
|
||||
|
||||
if (!target.IsAlive())
|
||||
return;
|
||||
|
||||
if (target.HasUnitState(UnitState.Isolated))
|
||||
@@ -5158,51 +5150,28 @@ namespace Game.Spells
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
// don't regen when permanent aura target has full power
|
||||
if (GetBase().IsPermanent() && target.IsFullHealth())
|
||||
return;
|
||||
|
||||
bool isAreaAura = GetSpellEffectInfo().IsAreaAuraEffect() || GetSpellEffectInfo().IsEffect(SpellEffectName.PersistentAreaAura);
|
||||
// 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)
|
||||
{
|
||||
// Taken mods
|
||||
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());
|
||||
damage = (uint)target.CountPctFromMaxHealth((int)damage);
|
||||
}
|
||||
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)
|
||||
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}",
|
||||
GetCasterGUID().ToString(), GetCaster().GetTypeId(), target.GetGUID().ToString(), target.GetTypeId(), damage, GetId());
|
||||
@@ -5210,13 +5179,14 @@ namespace Game.Spells
|
||||
uint heal = (uint)damage;
|
||||
|
||||
HealInfo healInfo = new(caster, target, heal, GetSpellInfo(), GetSpellInfo().GetSchoolMask());
|
||||
caster.CalcHealAbsorb(healInfo);
|
||||
caster.DealHeal(healInfo);
|
||||
Unit.CalcHealAbsorb(healInfo);
|
||||
Unit.DealHeal(healInfo);
|
||||
|
||||
SpellPeriodicAuraLogInfo pInfo = new(this, heal, (uint)damage, heal - healInfo.GetEffectiveHeal(), healInfo.GetAbsorb(), 0, 0.0f, crit);
|
||||
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
|
||||
if (GetAuraType() == AuraType.ObsModHealth)
|
||||
@@ -5227,7 +5197,7 @@ namespace Game.Spells
|
||||
ProcFlagsHit hitMask = crit ? ProcFlagsHit.Critical : ProcFlagsHit.Normal;
|
||||
// ignore item heals
|
||||
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)
|
||||
@@ -5248,7 +5218,7 @@ namespace Game.Spells
|
||||
return;
|
||||
|
||||
// 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);
|
||||
float gainMultiplier = GetSpellEffectInfo().CalcValueMultiplier(caster);
|
||||
@@ -5307,7 +5277,7 @@ namespace Game.Spells
|
||||
return;
|
||||
|
||||
// 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);
|
||||
|
||||
@@ -5336,7 +5306,7 @@ namespace Game.Spells
|
||||
return;
|
||||
|
||||
// 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);
|
||||
int gain = target.ModifyPower(powerType, amount);
|
||||
@@ -5361,7 +5331,7 @@ namespace Game.Spells
|
||||
}
|
||||
|
||||
// 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));
|
||||
|
||||
@@ -5373,7 +5343,7 @@ namespace Game.Spells
|
||||
// no SpellDamageBonus for burn mana
|
||||
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
|
||||
ProcFlags procAttacker = ProcFlags.DonePeriodic;
|
||||
@@ -5389,7 +5359,7 @@ namespace Game.Spells
|
||||
caster.DealSpellDamage(damageInfo, true);
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -5452,7 +5422,7 @@ namespace Game.Spells
|
||||
int damage = (int)target.SpellDamageBonusDone(triggerTarget, GetSpellInfo(), (uint)GetAmount(), DamageEffectType.SpellDirect, GetSpellEffectInfo());
|
||||
damage = (int)triggerTarget.SpellDamageBonusTaken(target, GetSpellInfo(), (uint)damage, DamageEffectType.SpellDirect, GetSpellEffectInfo());
|
||||
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.SendSpellNonMeleeDamageLog(damageInfo);
|
||||
}
|
||||
|
||||
+12
-13
@@ -1947,7 +1947,7 @@ namespace Game.Spells
|
||||
if (crit)
|
||||
{
|
||||
hitMask |= ProcFlagsHit.Critical;
|
||||
addhealth = (uint)caster.SpellCriticalHealingBonus(m_spellInfo, (int)addhealth, null);
|
||||
addhealth = (uint)Unit.SpellCriticalHealingBonus(caster, m_spellInfo, (int)addhealth, null);
|
||||
}
|
||||
else
|
||||
hitMask |= ProcFlagsHit.Normal;
|
||||
@@ -1959,7 +1959,7 @@ namespace Game.Spells
|
||||
|
||||
// Do triggers for unit
|
||||
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
|
||||
else if (m_damage > 0)
|
||||
@@ -1978,7 +1978,7 @@ namespace Game.Spells
|
||||
{
|
||||
// Add bonuses and fill damageInfo struct
|
||||
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);
|
||||
procVictim |= ProcFlags.TakenDamage;
|
||||
@@ -1995,7 +1995,7 @@ namespace Game.Spells
|
||||
if (canEffectTrigger)
|
||||
{
|
||||
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) &&
|
||||
(m_spellInfo.DmgClass == SpellDmgClass.Melee || m_spellInfo.DmgClass == SpellDmgClass.Ranged))
|
||||
@@ -2012,7 +2012,7 @@ namespace Game.Spells
|
||||
if (canEffectTrigger)
|
||||
{
|
||||
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) &&
|
||||
(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))
|
||||
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
|
||||
Creature caster = m_originalCaster.ToCreature();
|
||||
@@ -3187,7 +3187,7 @@ namespace Game.Spells
|
||||
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()
|
||||
@@ -6865,11 +6865,9 @@ namespace Game.Spells
|
||||
{
|
||||
if (effect.IsTargetingArea() || effect.IsAreaAuraEffect() || effect.IsEffect(SpellEffectName.PersistentAreaAura))
|
||||
{
|
||||
m_damage = (int)(m_damage * unit.GetTotalAuraMultiplierByMiscMask(AuraType.ModAoeDamageAvoidance, (uint)m_spellInfo.SchoolMask));
|
||||
if (!m_caster.IsTypeId(TypeId.Player))
|
||||
m_damage = (int)(m_damage * unit.GetTotalAuraMultiplierByMiscMask(AuraType.ModCreatureAoeDamageAvoidance, (uint)m_spellInfo.SchoolMask));
|
||||
m_damage = unit.CalculateAOEAvoidance(m_damage, (uint)m_spellInfo.SchoolMask, m_caster);
|
||||
|
||||
if (m_caster.IsTypeId(TypeId.Player))
|
||||
if (m_caster.IsPlayer())
|
||||
{
|
||||
long targetAmount = GetUnitTargetCountForEffect(effect.EffectIndex);
|
||||
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)
|
||||
@@ -8163,7 +8162,7 @@ namespace Game.Spells
|
||||
ProcFlagsSpellPhase spellPhaseMask = ProcFlagsSpellPhase.None;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ namespace Game.Spells
|
||||
data.SpellID = m_spellInfo.Id;
|
||||
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)]
|
||||
@@ -128,7 +128,7 @@ namespace Game.Spells
|
||||
else
|
||||
{
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
damage = (int)(bonus + (bonus * _variance));
|
||||
addhealth = (int)(bonus + (uint)(bonus * _variance));
|
||||
}
|
||||
|
||||
addhealth = (int)unitTarget.SpellHealingBonusTaken(caster, m_spellInfo, (uint)addhealth, DamageEffectType.Heal, effectInfo);
|
||||
@@ -937,9 +936,7 @@ namespace Game.Spells
|
||||
return;
|
||||
|
||||
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)heal;
|
||||
m_healing += (int)unitTarget.SpellHealingBonusTaken(m_originalCaster, m_spellInfo, heal, DamageEffectType.Heal, effectInfo);
|
||||
}
|
||||
|
||||
[SpellEffectHandler(SpellEffectName.HealMechanical)]
|
||||
@@ -2705,9 +2702,9 @@ namespace Game.Spells
|
||||
int duration = m_spellInfo.GetDuration();
|
||||
unitTarget.GetSpellHistory().LockSpellSchool(curSpellInfo.GetSchoolMask(), (uint)unitTarget.ModSpellDuration(m_spellInfo, unitTarget, duration, false, (uint)(1 << (int)effIndex)));
|
||||
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)
|
||||
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);
|
||||
unitTarget.InterruptSpell(i, false);
|
||||
|
||||
@@ -2986,6 +2986,14 @@ namespace Game.Entities
|
||||
spellInfo.GetEffect(0).TriggerSpell = 46737;
|
||||
spellInfo.GetEffect(0).ApplyAuraName = AuraType.PeriodicTriggerSpell;
|
||||
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 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);
|
||||
|
||||
@@ -271,7 +271,7 @@ namespace Scripts.Spells.DeathKnight
|
||||
int amount = (int)damageInfo.GetDamage() / 2;
|
||||
SpellNonMeleeDamage log = new SpellNonMeleeDamage(drw, drw.GetVictim(), spellInfo, new SpellCastVisual(spellInfo.GetSpellXSpellVisualId(drw), 0), spellInfo.GetSchoolMask());
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -287,7 +287,7 @@ namespace Scripts.Spells.Monk
|
||||
if (auraEff != null)
|
||||
{
|
||||
float total = auraEff.GetAmount();
|
||||
float tickDamage = aurEff.GetDamage();
|
||||
float tickDamage = aurEff.GetAmount();
|
||||
auraEff.ChangeAmount((int)(total - tickDamage));
|
||||
}
|
||||
}
|
||||
@@ -359,7 +359,7 @@ namespace Scripts.Spells.Monk
|
||||
{
|
||||
AuraEffect eff = auraDamage.GetEffect(0);
|
||||
if (eff != null)
|
||||
eff.SetDamage((int)tickDamage);
|
||||
eff.ChangeAmount((int)tickDamage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user