Core/Spells: rework part 5: GameObject casting
Port From (https://github.com/TrinityCore/TrinityCore/commit/962f6d7988b9003e550f6745be7cff812e9d8efa)
This commit is contained in:
@@ -631,43 +631,6 @@ namespace Game.Entities
|
||||
|
||||
public void SetBaseWeaponDamage(WeaponAttackType attType, WeaponDamageRange damageRange, float value) { m_weaponDamage[(int)attType][(int)damageRange] = value; }
|
||||
|
||||
public Unit GetMagicHitRedirectTarget(Unit victim, SpellInfo spellInfo)
|
||||
{
|
||||
// Patch 1.2 notes: Spell Reflection no longer reflects abilities
|
||||
if (spellInfo.HasAttribute(SpellAttr0.Ability) || spellInfo.HasAttribute(SpellAttr1.CantBeRedirected) || spellInfo.HasAttribute(SpellAttr0.UnaffectedByInvulnerability))
|
||||
return victim;
|
||||
|
||||
var magnetAuras = victim.GetAuraEffectsByType(AuraType.SpellMagnet);
|
||||
foreach (var eff in magnetAuras)
|
||||
{
|
||||
Unit magnet = eff.GetBase().GetCaster();
|
||||
if (magnet != null)
|
||||
{
|
||||
if (spellInfo.CheckExplicitTarget(this, magnet) == SpellCastResult.SpellCastOk && IsValidAttackTarget(magnet, spellInfo))
|
||||
{
|
||||
// @todo handle this charge drop by proc in cast phase on explicit target
|
||||
if (spellInfo.HasHitDelay())
|
||||
{
|
||||
// Set up missile speed based delay
|
||||
float hitDelay = spellInfo.LaunchDelay;
|
||||
if (spellInfo.HasAttribute(SpellAttr9.SpecialDelayCalculation))
|
||||
hitDelay += spellInfo.Speed;
|
||||
else if (spellInfo.Speed > 0.0f)
|
||||
hitDelay += Math.Max(victim.GetDistance(this), 5.0f) / spellInfo.Speed;
|
||||
|
||||
uint delay = (uint)Math.Floor(hitDelay * 1000.0f);
|
||||
// Schedule charge drop
|
||||
eff.GetBase().DropChargeDelayed(delay, AuraRemoveMode.Expire);
|
||||
}
|
||||
else
|
||||
eff.GetBase().DropCharge(AuraRemoveMode.Expire);
|
||||
return magnet;
|
||||
}
|
||||
}
|
||||
}
|
||||
return victim;
|
||||
}
|
||||
|
||||
public Unit GetMeleeHitRedirectTarget(Unit victim, SpellInfo spellInfo = null)
|
||||
{
|
||||
var interceptAuras = victim.GetAuraEffectsByType(AuraType.InterceptMeleeRangedAttacks);
|
||||
@@ -753,17 +716,6 @@ namespace Game.Entities
|
||||
}
|
||||
}
|
||||
|
||||
internal void SendCombatLogMessage(CombatLogServerPacket combatLog)
|
||||
{
|
||||
CombatLogSender combatLogSender = new(combatLog);
|
||||
|
||||
if (IsPlayer())
|
||||
combatLogSender.Invoke(ToPlayer());
|
||||
|
||||
var notifier = new MessageDistDeliverer<CombatLogSender>(this, combatLogSender, GetVisibilityRange());
|
||||
Cell.VisitWorldObjects(this, notifier, GetVisibilityRange());
|
||||
}
|
||||
|
||||
bool IsThreatened()
|
||||
{
|
||||
return !m_threatManager.IsThreatListEmpty();
|
||||
@@ -1562,286 +1514,6 @@ namespace Game.Entities
|
||||
}
|
||||
}
|
||||
|
||||
// function based on function Unit::CanAttack from 13850 client
|
||||
public bool IsValidAttackTarget(WorldObject target, SpellInfo bySpell = null, bool spellCheck = true)
|
||||
{
|
||||
Cypher.Assert(target != null);
|
||||
|
||||
// can't attack self
|
||||
if (this == target)
|
||||
return false;
|
||||
|
||||
// can't attack GMs
|
||||
if (target.IsPlayer() && target.ToPlayer().IsGameMaster())
|
||||
return false;
|
||||
|
||||
Unit unit = ToUnit();
|
||||
Unit targetUnit = target.ToUnit();
|
||||
|
||||
// CvC case - can attack each other only when one of them is hostile
|
||||
if (unit && !unit.HasUnitFlag(UnitFlags.PvpAttackable) && targetUnit && !targetUnit.HasUnitFlag(UnitFlags.PvpAttackable))
|
||||
return IsHostileTo(target) || target.IsHostileTo(this);
|
||||
|
||||
// PvP, PvC, CvP case
|
||||
// can't attack friendly targets
|
||||
if (IsFriendlyTo(target) || target.IsFriendlyTo(this))
|
||||
return false;
|
||||
|
||||
Player playerAffectingAttacker = unit && unit.HasUnitFlag(UnitFlags.PvpAttackable) ? GetAffectingPlayer() : null;
|
||||
Player playerAffectingTarget = targetUnit && targetUnit.HasUnitFlag(UnitFlags.PvpAttackable) ? target.GetAffectingPlayer() : null;
|
||||
|
||||
// Not all neutral creatures can be attacked (even some unfriendly faction does not react aggresive to you, like Sporaggar)
|
||||
if ((playerAffectingAttacker && !playerAffectingTarget) || (!playerAffectingAttacker && playerAffectingTarget))
|
||||
{
|
||||
Player player = playerAffectingAttacker ? playerAffectingAttacker : playerAffectingTarget;
|
||||
Unit creature = playerAffectingAttacker ? targetUnit : unit;
|
||||
if (creature != null)
|
||||
{
|
||||
if (creature.IsContestedGuard() && player.HasPlayerFlag(PlayerFlags.ContestedPVP))
|
||||
return true;
|
||||
|
||||
var factionTemplate = creature.GetFactionTemplateEntry();
|
||||
if (factionTemplate != null)
|
||||
{
|
||||
if (player.GetReputationMgr().GetForcedRankIfAny(factionTemplate) == 0)
|
||||
{
|
||||
var factionEntry = CliDB.FactionStorage.LookupByKey(factionTemplate.Faction);
|
||||
if (factionEntry != null)
|
||||
{
|
||||
var repState = player.GetReputationMgr().GetState(factionEntry);
|
||||
if (repState != null)
|
||||
if (!repState.Flags.HasFlag(ReputationFlags.AtWar))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Creature creatureAttacker = ToCreature();
|
||||
if (creatureAttacker && creatureAttacker.GetCreatureTemplate().TypeFlags.HasFlag(CreatureTypeFlags.TreatAsRaidUnit))
|
||||
return false;
|
||||
|
||||
if (bySpell == null)
|
||||
spellCheck = false;
|
||||
|
||||
if (spellCheck && !IsValidSpellAttackTarget(target, bySpell))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsValidSpellAttackTarget(WorldObject target, SpellInfo bySpell)
|
||||
{
|
||||
Cypher.Assert(target != null);
|
||||
Cypher.Assert(bySpell != null);
|
||||
|
||||
// can't attack unattackable units
|
||||
Unit unitTarget = target.ToUnit();
|
||||
if (unitTarget && unitTarget.HasUnitState(UnitState.Unattackable))
|
||||
return false;
|
||||
|
||||
Unit unit = ToUnit();
|
||||
// visibility checks (only units)
|
||||
if (unit)
|
||||
{
|
||||
// can't attack invisible
|
||||
if (!bySpell.HasAttribute(SpellAttr6.CanTargetInvisible))
|
||||
{
|
||||
if (!unit.CanSeeOrDetect(target, bySpell.IsAffectingArea()))
|
||||
return false;
|
||||
|
||||
/*
|
||||
else if (!obj)
|
||||
{
|
||||
// ignore stealth for aoe spells. Ignore stealth if target is player and unit in combat with same player
|
||||
bool const ignoreStealthCheck = (bySpell && bySpell.IsAffectingArea()) ||
|
||||
(target.GetTypeId() == TYPEID_PLAYER && target.HasStealthAura() && IsInCombatWith(target));
|
||||
|
||||
if (!CanSeeOrDetect(target, ignoreStealthCheck))
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
// can't attack dead
|
||||
if (!bySpell.IsAllowingDeadTarget() && unitTarget && !unitTarget.IsAlive())
|
||||
return false;
|
||||
|
||||
// can't attack untargetable
|
||||
if (!bySpell.HasAttribute(SpellAttr6.CanTargetUntargetable) && unitTarget && unitTarget.HasUnitFlag(UnitFlags.NotSelectable))
|
||||
return false;
|
||||
|
||||
Player playerAttacker = ToPlayer();
|
||||
if (playerAttacker != null)
|
||||
{
|
||||
if (playerAttacker.HasPlayerFlag(PlayerFlags.Uber))
|
||||
return false;
|
||||
}
|
||||
|
||||
// check flags
|
||||
if (unitTarget != null && unitTarget.HasUnitFlag(UnitFlags.NonAttackable | UnitFlags.TaxiFlight | UnitFlags.NotAttackable1 | UnitFlags.Unk16))
|
||||
return false;
|
||||
|
||||
if (unit != null && !unit.HasUnitFlag(UnitFlags.PvpAttackable) && unitTarget && unitTarget.IsImmuneToNPC())
|
||||
return false;
|
||||
|
||||
if (unitTarget != null && !unitTarget.HasUnitFlag(UnitFlags.PvpAttackable) && unit && unit.IsImmuneToNPC())
|
||||
return false;
|
||||
|
||||
if (!bySpell.HasAttribute(SpellAttr8.AttackIgnoreImmuneToPCFlag))
|
||||
{
|
||||
if (unit && unit.HasUnitFlag(UnitFlags.PvpAttackable) && unitTarget && unitTarget.IsImmuneToPC())
|
||||
return false;
|
||||
|
||||
if (unitTarget && unitTarget.HasUnitFlag(UnitFlags.PvpAttackable) && unit && unit.IsImmuneToPC())
|
||||
return false;
|
||||
}
|
||||
|
||||
// check duel - before sanctuary checks
|
||||
Player playerAffectingAttacker = unit && unit.HasUnitFlag(UnitFlags.PvpAttackable) ? GetAffectingPlayer() : null;
|
||||
Player playerAffectingTarget = unitTarget && unitTarget.HasUnitFlag(UnitFlags.PvpAttackable) ? target.GetAffectingPlayer() : null;
|
||||
if (playerAffectingAttacker && playerAffectingTarget)
|
||||
if (playerAffectingAttacker.duel != null && playerAffectingAttacker.duel.opponent == playerAffectingTarget && playerAffectingAttacker.duel.startTime != 0)
|
||||
return true;
|
||||
|
||||
// PvP case - can't attack when attacker or target are in sanctuary
|
||||
// however, 13850 client doesn't allow to attack when one of the unit's has sanctuary flag and is pvp
|
||||
if (unitTarget && unitTarget.HasUnitFlag(UnitFlags.PvpAttackable) && unit && unit.HasUnitFlag(UnitFlags.PvpAttackable) && (unitTarget.IsInSanctuary() || unit.IsInSanctuary()))
|
||||
return false;
|
||||
|
||||
// additional checks - only PvP case
|
||||
if (playerAffectingAttacker && playerAffectingTarget)
|
||||
{
|
||||
if (unitTarget.IsPvP())
|
||||
return true;
|
||||
|
||||
if (unit.IsFFAPvP() && unitTarget.IsFFAPvP())
|
||||
return true;
|
||||
|
||||
return unit.HasPvpFlag(UnitPVPStateFlags.Unk1) ||
|
||||
unitTarget.HasPvpFlag(UnitPVPStateFlags.Unk1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// function based on function Unit::CanAssist from 13850 client
|
||||
public bool IsValidAssistTarget(WorldObject target, SpellInfo bySpell = null, bool spellCheck = true)
|
||||
{
|
||||
Cypher.Assert(target != null);
|
||||
|
||||
// can assist to self
|
||||
if (this == target)
|
||||
return true;
|
||||
|
||||
// can't assist GMs
|
||||
if (target.IsPlayer() && target.ToPlayer().IsGameMaster())
|
||||
return false;
|
||||
|
||||
// can't assist non-friendly targets
|
||||
if (GetReactionTo(target) < ReputationRank.Neutral && target.GetReactionTo(this) < ReputationRank.Neutral && (!ToCreature() || !ToCreature().GetCreatureTemplate().TypeFlags.HasFlag(CreatureTypeFlags.TreatAsRaidUnit)))
|
||||
return false;
|
||||
|
||||
if (bySpell == null)
|
||||
spellCheck = false;
|
||||
|
||||
if (spellCheck && !IsValidSpellAssistTarget(target, bySpell))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsValidSpellAssistTarget(WorldObject target, SpellInfo bySpell)
|
||||
{
|
||||
Cypher.Assert(target != null);
|
||||
Cypher.Assert(bySpell != null);
|
||||
|
||||
// can't assist unattackable units
|
||||
Unit unitTarget = target.ToUnit();
|
||||
if (unitTarget && unitTarget.HasUnitState(UnitState.Unattackable))
|
||||
return false;
|
||||
|
||||
// can't assist own vehicle or passenger
|
||||
Unit unit = ToUnit();
|
||||
if (unit && unitTarget && unit.GetVehicle())
|
||||
{
|
||||
if (unit.IsOnVehicle(unitTarget))
|
||||
return false;
|
||||
|
||||
if (unit.GetVehicleBase().IsOnVehicle(unitTarget))
|
||||
return false;
|
||||
}
|
||||
|
||||
// can't assist invisible
|
||||
if (!bySpell.HasAttribute(SpellAttr6.CanTargetInvisible) && !CanSeeOrDetect(target, bySpell.IsAffectingArea()))
|
||||
return false;
|
||||
|
||||
// can't assist dead
|
||||
if (!bySpell.IsAllowingDeadTarget() && unitTarget && !unitTarget.IsAlive())
|
||||
return false;
|
||||
|
||||
// can't assist untargetable
|
||||
if (!bySpell.HasAttribute(SpellAttr6.CanTargetUntargetable) && unitTarget && unitTarget.HasUnitFlag(UnitFlags.NotSelectable))
|
||||
return false;
|
||||
|
||||
if (!bySpell.HasAttribute(SpellAttr6.AssistIgnoreImmuneFlag))
|
||||
{
|
||||
if (unit && unit.HasUnitFlag(UnitFlags.PvpAttackable))
|
||||
{
|
||||
if (unitTarget && unitTarget.IsImmuneToPC())
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (unitTarget && unitTarget.IsImmuneToNPC())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// PvP case
|
||||
if (unitTarget && unitTarget.HasUnitFlag(UnitFlags.PvpAttackable))
|
||||
{
|
||||
Player targetPlayerOwner = target.GetAffectingPlayer();
|
||||
if (unit && unit.HasUnitFlag(UnitFlags.PvpAttackable))
|
||||
{
|
||||
Player selfPlayerOwner = GetAffectingPlayer();
|
||||
if (selfPlayerOwner && targetPlayerOwner)
|
||||
{
|
||||
// can't assist player which is dueling someone
|
||||
if (selfPlayerOwner != targetPlayerOwner && targetPlayerOwner.duel != null)
|
||||
return false;
|
||||
}
|
||||
// can't assist player in ffa_pvp zone from outside
|
||||
if (unitTarget.IsFFAPvP() && unit && !unit.IsFFAPvP())
|
||||
return false;
|
||||
|
||||
// can't assist player out of sanctuary from sanctuary if has pvp enabled
|
||||
if (unitTarget.IsPvP())
|
||||
if (unit && unit.IsInSanctuary() && !unitTarget.IsInSanctuary())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// PvC case - player can assist creature only if has specific type flags
|
||||
// !target.HasFlag(UNIT_FIELD_FLAGS, UnitFlags.PvpAttackable) &&
|
||||
else if (unit && unit.HasUnitFlag(UnitFlags.PvpAttackable))
|
||||
{
|
||||
if (!bySpell.HasAttribute(SpellAttr6.AssistIgnoreImmuneFlag))
|
||||
{
|
||||
if (unitTarget && !unitTarget.IsPvP())
|
||||
{
|
||||
Creature creatureTarget = target.ToCreature();
|
||||
if (creatureTarget != null)
|
||||
return creatureTarget.GetCreatureTemplate().TypeFlags.HasFlag(CreatureTypeFlags.TreatAsRaidUnit) || creatureTarget.GetCreatureTemplate().TypeFlags.HasFlag(CreatureTypeFlags.CanAssist);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual bool CheckAttackFitToAuraRequirement(WeaponAttackType attackType, AuraEffect aurEff) { return true; }
|
||||
|
||||
public void ApplyAttackTimePercentMod(WeaponAttackType att, float val, bool apply)
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace Game.Entities
|
||||
protected float[][] m_weaponDamage = new float[(int)WeaponAttackType.Max][];
|
||||
|
||||
uint[] m_baseAttackSpeed = new uint[(int)WeaponAttackType.Max];
|
||||
float[] m_modAttackSpeedPct = new float[(int)WeaponAttackType.Max];
|
||||
internal float[] m_modAttackSpeedPct = new float[(int)WeaponAttackType.Max];
|
||||
protected uint[] m_attackTimer = new uint[(int)WeaponAttackType.Max];
|
||||
|
||||
CombatManager m_combatManager;
|
||||
@@ -117,7 +117,6 @@ namespace Game.Entities
|
||||
float[] m_floatStatNegBuff = new float[(int)Stats.Max];
|
||||
public ObjectGuid[] m_SummonSlot = new ObjectGuid[7];
|
||||
public ObjectGuid[] m_ObjectSlot = new ObjectGuid[4];
|
||||
public EventSystem m_Events = new();
|
||||
public UnitTypeMask UnitTypeMask { get; set; }
|
||||
UnitState m_state;
|
||||
protected LiquidTypeRecord _lastLiquid;
|
||||
@@ -498,14 +497,14 @@ namespace Game.Entities
|
||||
|
||||
public class DispelInfo
|
||||
{
|
||||
public DispelInfo(Unit dispeller, uint dispellerSpellId, byte chargesRemoved)
|
||||
public DispelInfo(WorldObject dispeller, uint dispellerSpellId, byte chargesRemoved)
|
||||
{
|
||||
_dispellerUnit = dispeller;
|
||||
_dispeller = dispeller;
|
||||
_dispellerSpell = dispellerSpellId;
|
||||
_chargesRemoved = chargesRemoved;
|
||||
}
|
||||
|
||||
public Unit GetDispeller() { return _dispellerUnit; }
|
||||
public WorldObject GetDispeller() { return _dispeller; }
|
||||
uint GetDispellerSpellId() { return _dispellerSpell; }
|
||||
public byte GetRemovedCharges() { return _chargesRemoved; }
|
||||
public void SetRemovedCharges(byte amount)
|
||||
@@ -513,7 +512,7 @@ namespace Game.Entities
|
||||
_chargesRemoved = amount;
|
||||
}
|
||||
|
||||
Unit _dispellerUnit;
|
||||
WorldObject _dispeller;
|
||||
uint _dispellerSpell;
|
||||
byte _chargesRemoved;
|
||||
}
|
||||
@@ -554,22 +553,4 @@ namespace Game.Entities
|
||||
{
|
||||
public StringArray name = new(SharedConst.MaxDeclinedNameCases);
|
||||
}
|
||||
|
||||
class CombatLogSender : IDoWork<Player>
|
||||
{
|
||||
CombatLogServerPacket i_message;
|
||||
|
||||
public CombatLogSender(CombatLogServerPacket msg)
|
||||
{
|
||||
i_message = msg;
|
||||
}
|
||||
|
||||
public void Invoke(Player player)
|
||||
{
|
||||
i_message.Clear();
|
||||
i_message.SetAdvancedCombatLogging(player.IsAdvancedCombatLoggingEnabled());
|
||||
|
||||
player.SendPacket(i_message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,19 +32,6 @@ namespace Game.Entities
|
||||
|
||||
public void SetInstantCast(bool set) { _instantCast = set; }
|
||||
public bool CanInstantCast() { return _instantCast; }
|
||||
|
||||
// function uses real base points (typically value - 1)
|
||||
public int CalculateSpellDamage(Unit target, SpellInfo spellProto, uint effect_index, int? basePoints = null, uint castItemId = 0, int itemLevel = -1)
|
||||
{
|
||||
SpellEffectInfo effect = spellProto.GetEffect(effect_index);
|
||||
return effect != null ? effect.CalcValue(this, basePoints, target, castItemId, itemLevel) : 0;
|
||||
}
|
||||
public int CalculateSpellDamage(Unit target, SpellInfo spellProto, uint effect_index, out float variance, int? basePoints = null, uint castItemId = 0, int itemLevel = -1)
|
||||
{
|
||||
SpellEffectInfo effect = spellProto.GetEffect(effect_index);
|
||||
variance = 0.0f;
|
||||
return effect != null ? effect.CalcValue(out variance, this, basePoints, target, castItemId, itemLevel) : 0;
|
||||
}
|
||||
|
||||
public int SpellBaseDamageBonusDone(SpellSchoolMask schoolMask)
|
||||
{
|
||||
@@ -754,66 +741,8 @@ namespace Game.Entities
|
||||
return Math.Max(crit_chance, 0.0f);
|
||||
}
|
||||
|
||||
// Calculate spell hit result can be:
|
||||
// Every spell can: Evade/Immune/Reflect/Sucesful hit
|
||||
// For melee based spells:
|
||||
// Miss
|
||||
// Dodge
|
||||
// Parry
|
||||
// For spells
|
||||
// Resist
|
||||
public SpellMissInfo SpellHitResult(Unit victim, SpellInfo spellInfo, bool canReflect = false)
|
||||
{
|
||||
// All positive spells can`t miss
|
||||
// @todo client not show miss log for this spells - so need find info for this in dbc and use it!
|
||||
if (spellInfo.IsPositive()
|
||||
&& (!IsHostileTo(victim))) // prevent from affecting enemy by "positive" spell
|
||||
return SpellMissInfo.None;
|
||||
|
||||
if (this == victim)
|
||||
return SpellMissInfo.None;
|
||||
|
||||
// Return evade for units in evade mode
|
||||
if (victim.IsTypeId(TypeId.Unit) && victim.ToCreature().IsEvadingAttacks())
|
||||
return SpellMissInfo.Evade;
|
||||
|
||||
// Try victim reflect spell
|
||||
if (canReflect)
|
||||
{
|
||||
int reflectchance = victim.GetTotalAuraModifier(AuraType.ReflectSpells);
|
||||
reflectchance += victim.GetTotalAuraModifierByMiscMask(AuraType.ReflectSpellsSchool, (int)spellInfo.GetSchoolMask());
|
||||
|
||||
if (reflectchance > 0 && RandomHelper.randChance(reflectchance))
|
||||
return SpellMissInfo.Reflect;
|
||||
}
|
||||
|
||||
if (spellInfo.HasAttribute(SpellAttr3.IgnoreHitResult))
|
||||
return SpellMissInfo.None;
|
||||
|
||||
// Check for immune
|
||||
if (victim.IsImmunedToSpell(spellInfo, this))
|
||||
return SpellMissInfo.Immune;
|
||||
|
||||
// Damage immunity is only checked if the spell has damage effects, this immunity must not prevent aura apply
|
||||
// returns SPELL_MISS_IMMUNE in that case, for other spells, the SMSG_SPELL_GO must show hit
|
||||
if (spellInfo.HasOnlyDamageEffects() && victim.IsImmunedToDamage(spellInfo))
|
||||
return SpellMissInfo.Immune;
|
||||
|
||||
switch (spellInfo.DmgClass)
|
||||
{
|
||||
case SpellDmgClass.Ranged:
|
||||
case SpellDmgClass.Melee:
|
||||
return MeleeSpellHitResult(victim, spellInfo);
|
||||
case SpellDmgClass.None:
|
||||
return SpellMissInfo.None;
|
||||
case SpellDmgClass.Magic:
|
||||
return MagicSpellHitResult(victim, spellInfo);
|
||||
}
|
||||
return SpellMissInfo.None;
|
||||
}
|
||||
|
||||
// Melee based spells hit result calculations
|
||||
SpellMissInfo MeleeSpellHitResult(Unit victim, SpellInfo spellInfo)
|
||||
public override SpellMissInfo MeleeSpellHitResult(Unit victim, SpellInfo spellInfo)
|
||||
{
|
||||
WeaponAttackType attType = WeaponAttackType.BaseAttack;
|
||||
|
||||
@@ -949,142 +878,6 @@ namespace Game.Entities
|
||||
return SpellMissInfo.None;
|
||||
}
|
||||
|
||||
SpellMissInfo MagicSpellHitResult(Unit victim, SpellInfo spell)
|
||||
{
|
||||
// Can`t miss on dead target (on skinning for example)
|
||||
if (!victim.IsAlive() && !victim.IsTypeId(TypeId.Player))
|
||||
return SpellMissInfo.None;
|
||||
|
||||
SpellSchoolMask schoolMask = spell.GetSchoolMask();
|
||||
// PvP - PvE spell misschances per leveldif > 2
|
||||
int lchance = victim.IsTypeId(TypeId.Player) ? 7 : 11;
|
||||
int thisLevel = (int)GetLevelForTarget(victim);
|
||||
if (IsTypeId(TypeId.Unit) && ToCreature().IsTrigger())
|
||||
thisLevel = (int)Math.Max(thisLevel, spell.SpellLevel);
|
||||
int leveldif = (int)(victim.GetLevelForTarget(this)) - thisLevel;
|
||||
int levelBasedHitDiff = leveldif;
|
||||
|
||||
// Base hit chance from attacker and victim levels
|
||||
int modHitChance;
|
||||
if (levelBasedHitDiff >= 0)
|
||||
{
|
||||
if (!victim.IsTypeId(TypeId.Player))
|
||||
{
|
||||
modHitChance = 94 - 3 * Math.Min(levelBasedHitDiff, 3);
|
||||
levelBasedHitDiff -= 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
modHitChance = 96 - Math.Min(levelBasedHitDiff, 2);
|
||||
levelBasedHitDiff -= 2;
|
||||
}
|
||||
if (levelBasedHitDiff > 0)
|
||||
modHitChance -= lchance * Math.Min(levelBasedHitDiff, 7);
|
||||
}
|
||||
else
|
||||
modHitChance = 97 - levelBasedHitDiff;
|
||||
|
||||
// Spellmod from SpellModOp.HitChance
|
||||
Player modOwner = GetSpellModOwner();
|
||||
if (modOwner != null)
|
||||
modOwner.ApplySpellMod(spell, SpellModOp.HitChance, ref modHitChance);
|
||||
|
||||
// Spells with SPELL_ATTR3_IGNORE_HIT_RESULT will ignore target's avoidance effects
|
||||
if (!spell.HasAttribute(SpellAttr3.IgnoreHitResult))
|
||||
{
|
||||
// Chance hit from victim SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE auras
|
||||
modHitChance += victim.GetTotalAuraModifierByMiscMask(AuraType.ModAttackerSpellHitChance, (int)schoolMask);
|
||||
}
|
||||
|
||||
int HitChance = modHitChance * 100;
|
||||
// Increase hit chance from attacker SPELL_AURA_MOD_SPELL_HIT_CHANCE and attacker ratings
|
||||
HitChance += (int)(modHitChance * 100.0f);
|
||||
|
||||
MathFunctions.RoundToInterval(ref HitChance, 0, 10000);
|
||||
|
||||
int tmp = 10000 - HitChance;
|
||||
|
||||
int rand = RandomHelper.IRand(0, 9999);
|
||||
if (tmp > 0 && rand < tmp)
|
||||
return SpellMissInfo.Miss;
|
||||
|
||||
// Spells with SPELL_ATTR3_IGNORE_HIT_RESULT will additionally fully ignore
|
||||
// resist and deflect chances
|
||||
if (spell.HasAttribute(SpellAttr3.IgnoreHitResult))
|
||||
return SpellMissInfo.None;
|
||||
|
||||
// Chance resist mechanic (select max value from every mechanic spell effect)
|
||||
int resist_chance = victim.GetMechanicResistChance(spell) * 100;
|
||||
|
||||
// Roll chance
|
||||
if (resist_chance > 0 && rand < (tmp += resist_chance))
|
||||
return SpellMissInfo.Resist;
|
||||
|
||||
// cast by caster in front of victim
|
||||
if (!victim.HasUnitState(UnitState.Controlled) && (victim.HasInArc(MathFunctions.PI, this) || victim.HasAuraType(AuraType.IgnoreHitDirection)))
|
||||
{
|
||||
int deflect_chance = victim.GetTotalAuraModifier(AuraType.DeflectSpells) * 100;
|
||||
if (deflect_chance > 0 && rand < (tmp += deflect_chance))
|
||||
return SpellMissInfo.Deflect;
|
||||
}
|
||||
|
||||
return SpellMissInfo.None;
|
||||
}
|
||||
|
||||
public void CastSpell(SpellCastTargets targets, uint spellId, CastSpellExtraArgs args)
|
||||
{
|
||||
if (args == null)
|
||||
args = new CastSpellExtraArgs();
|
||||
|
||||
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(spellId, args.CastDifficulty != Difficulty.None ? args.CastDifficulty : GetMap().GetDifficultyID());
|
||||
if (spellInfo == null)
|
||||
{
|
||||
Log.outError(LogFilter.Unit, $"CastSpell: unknown spell {spellId} by caster: {GetGUID()}");
|
||||
return;
|
||||
}
|
||||
|
||||
Spell spell = new(this, spellInfo, args.TriggerFlags, args.OriginalCaster);
|
||||
foreach (var pair in args.SpellValueOverrides)
|
||||
spell.SetSpellValue(pair.Key, pair.Value);
|
||||
|
||||
spell.m_CastItem = args.CastItem;
|
||||
spell.Prepare(targets, args.TriggeringAura);
|
||||
}
|
||||
|
||||
public void CastSpell(WorldObject target, uint spellId, bool triggered)
|
||||
{
|
||||
CastSpell(target, spellId, new CastSpellExtraArgs(triggered));
|
||||
}
|
||||
|
||||
public void CastSpell(WorldObject target, uint spellId, CastSpellExtraArgs args = null)
|
||||
{
|
||||
SpellCastTargets targets = new();
|
||||
if (target)
|
||||
{
|
||||
Unit unitTarget = target.ToUnit();
|
||||
GameObject goTarget = target.ToGameObject();
|
||||
if (unitTarget != null)
|
||||
targets.SetUnitTarget(unitTarget);
|
||||
else if (goTarget != null)
|
||||
targets.SetGOTarget(goTarget);
|
||||
else
|
||||
{
|
||||
Log.outError(LogFilter.Unit, $"CastSpell: Invalid target {target.GetGUID()} passed to spell cast by {GetGUID()}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
CastSpell(targets, spellId, args);
|
||||
}
|
||||
|
||||
public void CastSpell(Position dest, uint spellId, CastSpellExtraArgs args = null)
|
||||
{
|
||||
SpellCastTargets targets = new();
|
||||
targets.SetDst(dest);
|
||||
|
||||
CastSpell(targets, spellId, args);
|
||||
}
|
||||
|
||||
public void FinishSpell(CurrentSpellTypes spellType, bool ok = true)
|
||||
{
|
||||
Spell spell = GetCurrentSpell(spellType);
|
||||
@@ -1223,7 +1016,7 @@ namespace Game.Entities
|
||||
return spellInfo;
|
||||
}
|
||||
|
||||
public uint GetCastSpellXSpellVisualId(SpellInfo spellInfo)
|
||||
public override uint GetCastSpellXSpellVisualId(SpellInfo spellInfo)
|
||||
{
|
||||
var visualOverrides = GetAuraEffectsByType(AuraType.OverrideSpellVisual);
|
||||
foreach (AuraEffect effect in visualOverrides)
|
||||
@@ -1239,7 +1032,7 @@ namespace Game.Entities
|
||||
}
|
||||
}
|
||||
|
||||
return spellInfo.GetSpellXSpellVisualId(this);
|
||||
return base.GetCastSpellXSpellVisualId(spellInfo);
|
||||
}
|
||||
|
||||
public SpellHistory GetSpellHistory() { return _spellHistory; }
|
||||
@@ -1520,7 +1313,7 @@ namespace Game.Entities
|
||||
}
|
||||
}
|
||||
}
|
||||
public virtual bool IsImmunedToSpell(SpellInfo spellInfo, Unit caster)
|
||||
public virtual bool IsImmunedToSpell(SpellInfo spellInfo, WorldObject caster)
|
||||
{
|
||||
if (spellInfo == null)
|
||||
return false;
|
||||
@@ -1617,7 +1410,7 @@ namespace Game.Entities
|
||||
|
||||
return mask;
|
||||
}
|
||||
public virtual bool IsImmunedToSpellEffect(SpellInfo spellInfo, uint index, Unit caster)
|
||||
public virtual bool IsImmunedToSpellEffect(SpellInfo spellInfo, uint index, WorldObject caster)
|
||||
{
|
||||
if (spellInfo == null)
|
||||
return false;
|
||||
@@ -1887,88 +1680,11 @@ namespace Game.Entities
|
||||
if (GetCurrentSpell(i) != null && GetCurrentSpell(i).m_spellInfo.Id != except_spellid)
|
||||
InterruptSpell(i, false);
|
||||
}
|
||||
public void ModSpellCastTime(SpellInfo spellInfo, ref int castTime, Spell spell = null)
|
||||
{
|
||||
if (spellInfo == null || castTime < 0)
|
||||
return;
|
||||
|
||||
// called from caster
|
||||
Player modOwner = GetSpellModOwner();
|
||||
if (modOwner != null)
|
||||
modOwner.ApplySpellMod(spellInfo, SpellModOp.ChangeCastTime, ref castTime, spell);
|
||||
|
||||
if (!(spellInfo.HasAttribute(SpellAttr0.Ability | SpellAttr0.Tradespell) || spellInfo.HasAttribute(SpellAttr3.NoDoneBonus))
|
||||
&& (IsTypeId(TypeId.Player) && spellInfo.SpellFamilyName != 0) || IsTypeId(TypeId.Unit))
|
||||
castTime = CanInstantCast() ? 0 : (int)(castTime * m_unitData.ModCastingSpeed);
|
||||
else if (spellInfo.HasAttribute(SpellAttr0.ReqAmmo) && !spellInfo.HasAttribute(SpellAttr2.AutorepeatFlag))
|
||||
castTime = (int)(castTime * m_modAttackSpeedPct[(int)WeaponAttackType.RangedAttack]);
|
||||
else if (Global.SpellMgr.IsPartOfSkillLine(SkillType.Cooking, spellInfo.Id) && HasAura(67556)) // cooking with Chef Hat.
|
||||
castTime = 500;
|
||||
}
|
||||
public void ModSpellDurationTime(SpellInfo spellInfo, ref int duration, Spell spell = null)
|
||||
{
|
||||
if (spellInfo == null || duration < 0)
|
||||
return;
|
||||
|
||||
if (spellInfo.IsChanneled() && !spellInfo.HasAttribute(SpellAttr5.HasteAffectDuration))
|
||||
return;
|
||||
|
||||
// called from caster
|
||||
Player modOwner = GetSpellModOwner();
|
||||
if (modOwner)
|
||||
modOwner.ApplySpellMod(spellInfo, SpellModOp.ChangeCastTime, ref duration, spell);
|
||||
|
||||
if (!(spellInfo.HasAttribute(SpellAttr0.Ability) || spellInfo.HasAttribute(SpellAttr0.Tradespell) || spellInfo.HasAttribute(SpellAttr3.NoDoneBonus)) &&
|
||||
(IsTypeId(TypeId.Player) && spellInfo.SpellFamilyName != 0) || IsTypeId(TypeId.Unit))
|
||||
duration = (int)(duration * m_unitData.ModCastingSpeed);
|
||||
else if (spellInfo.HasAttribute(SpellAttr0.ReqAmmo) && !spellInfo.HasAttribute(SpellAttr2.AutorepeatFlag))
|
||||
duration = (int)(duration * m_modAttackSpeedPct[(int)WeaponAttackType.RangedAttack]);
|
||||
}
|
||||
public float ApplyEffectModifiers(SpellInfo spellProto, uint effect_index, float value)
|
||||
{
|
||||
Player modOwner = GetSpellModOwner();
|
||||
if (modOwner != null)
|
||||
{
|
||||
modOwner.ApplySpellMod(spellProto, SpellModOp.Points, ref value);
|
||||
switch (effect_index)
|
||||
{
|
||||
case 0:
|
||||
modOwner.ApplySpellMod(spellProto, SpellModOp.PointsIndex0, ref value);
|
||||
break;
|
||||
case 1:
|
||||
modOwner.ApplySpellMod(spellProto, SpellModOp.PointsIndex1, ref value);
|
||||
break;
|
||||
case 2:
|
||||
modOwner.ApplySpellMod(spellProto, SpellModOp.PointsIndex2, ref value);
|
||||
break;
|
||||
case 3:
|
||||
modOwner.ApplySpellMod(spellProto, SpellModOp.PointsIndex3, ref value);
|
||||
break;
|
||||
case 4:
|
||||
modOwner.ApplySpellMod(spellProto, SpellModOp.PointsIndex4, ref value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public ushort GetMaxSkillValueForLevel(Unit target = null)
|
||||
{
|
||||
return (ushort)(target != null ? GetLevelForTarget(target) : GetLevel() * 5);
|
||||
}
|
||||
public Player GetSpellModOwner()
|
||||
{
|
||||
if (IsTypeId(TypeId.Player))
|
||||
return ToPlayer();
|
||||
|
||||
if (HasUnitTypeMask(UnitTypeMask.Pet | UnitTypeMask.Totem | UnitTypeMask.Guardian))
|
||||
{
|
||||
Unit owner = GetOwner();
|
||||
if (owner != null && owner.IsTypeId(TypeId.Player))
|
||||
return owner.ToPlayer();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Spell GetCurrentSpell(CurrentSpellTypes spellType)
|
||||
{
|
||||
@@ -2283,7 +1999,6 @@ namespace Game.Entities
|
||||
|
||||
// update for out of range group members (on 1 slot use)
|
||||
aura.SetNeedClientUpdateForTargets();
|
||||
Log.outDebug(LogFilter.Spells, "Aura {0} partially interrupted on {1}, new duration: {2} ms", aura.GetId(), GetGUID().ToString(), aura.GetDuration());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2641,7 +2356,7 @@ namespace Game.Entities
|
||||
diminish.HitCount = currentLevel + 1;
|
||||
}
|
||||
|
||||
public bool ApplyDiminishingToDuration(SpellInfo auraSpellInfo, ref int duration, Unit caster, DiminishingLevels previousLevel)
|
||||
public bool ApplyDiminishingToDuration(SpellInfo auraSpellInfo, ref int duration, WorldObject caster, DiminishingLevels previousLevel)
|
||||
{
|
||||
DiminishingGroup group = auraSpellInfo.GetDiminishingReturnsGroupForSpell();
|
||||
if (duration == -1 || group == DiminishingGroup.None)
|
||||
@@ -2656,7 +2371,7 @@ namespace Game.Entities
|
||||
if (limitDuration > 0 && duration > limitDuration)
|
||||
{
|
||||
Unit target = targetOwner ?? this;
|
||||
Unit source = casterOwner ?? caster;
|
||||
WorldObject source = casterOwner ?? caster;
|
||||
|
||||
if (target.IsAffectedByDiminishingReturns() && source.IsPlayer())
|
||||
duration = limitDuration;
|
||||
@@ -2863,9 +2578,6 @@ namespace Game.Entities
|
||||
if (spellInfo == null)
|
||||
return null;
|
||||
|
||||
if (!target.IsAlive() && !spellInfo.IsPassive() && !spellInfo.HasAttribute(SpellAttr2.CanTargetDead))
|
||||
return null;
|
||||
|
||||
return AddAura(spellInfo, SpellConst.MaxEffectMask, target);
|
||||
}
|
||||
|
||||
@@ -2874,6 +2586,9 @@ namespace Game.Entities
|
||||
if (spellInfo == null)
|
||||
return null;
|
||||
|
||||
if (!target.IsAlive() && !spellInfo.IsPassive() && !spellInfo.HasAttribute(SpellAttr2.CanTargetDead))
|
||||
return null;
|
||||
|
||||
if (target.IsImmunedToSpell(spellInfo, this))
|
||||
return null;
|
||||
|
||||
@@ -3037,29 +2752,6 @@ namespace Game.Entities
|
||||
return false;
|
||||
}
|
||||
|
||||
// target dependent range checks
|
||||
public float GetSpellMaxRangeForTarget(Unit target, SpellInfo spellInfo)
|
||||
{
|
||||
if (spellInfo.RangeEntry == null)
|
||||
return 0;
|
||||
if (spellInfo.RangeEntry.RangeMax[0] == spellInfo.RangeEntry.RangeMax[1])
|
||||
return spellInfo.GetMaxRange();
|
||||
if (!target)
|
||||
return spellInfo.GetMaxRange(true);
|
||||
return spellInfo.GetMaxRange(!IsHostileTo(target));
|
||||
}
|
||||
|
||||
public float GetSpellMinRangeForTarget(Unit target, SpellInfo spellInfo)
|
||||
{
|
||||
if (spellInfo.RangeEntry == null)
|
||||
return 0;
|
||||
if (spellInfo.RangeEntry.RangeMin[0] == spellInfo.RangeEntry.RangeMin[1])
|
||||
return spellInfo.GetMinRange();
|
||||
if (!target)
|
||||
return spellInfo.GetMinRange(true);
|
||||
return spellInfo.GetMinRange(!IsHostileTo(target));
|
||||
}
|
||||
|
||||
public bool HasAuraType(AuraType auraType)
|
||||
{
|
||||
return !m_modAuras.LookupByKey(auraType).Empty();
|
||||
@@ -3177,7 +2869,7 @@ namespace Game.Entities
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<DispelableAura> GetDispellableAuraList(Unit caster, uint dispelMask, bool isReflect = false)
|
||||
public List<DispelableAura> GetDispellableAuraList(WorldObject caster, uint dispelMask, bool isReflect = false)
|
||||
{
|
||||
List<DispelableAura> dispelList = new();
|
||||
|
||||
@@ -3311,7 +3003,7 @@ namespace Game.Entities
|
||||
}
|
||||
}
|
||||
}
|
||||
public void RemoveAurasDueToSpellBySteal(uint spellId, ObjectGuid casterGUID, Unit stealer)
|
||||
public void RemoveAurasDueToSpellBySteal(uint spellId, ObjectGuid casterGUID, WorldObject stealer)
|
||||
{
|
||||
var range = m_ownedAuras.LookupByKey(spellId);
|
||||
foreach (var aura in range)
|
||||
@@ -3344,39 +3036,43 @@ namespace Game.Entities
|
||||
// Cast duration to unsigned to prevent permanent aura's such as Righteous Fury being permanently added to caster
|
||||
uint dur = (uint)Math.Min(2u * Time.Minute * Time.InMilliseconds, aura.GetDuration());
|
||||
|
||||
Aura oldAura = stealer.GetAura(aura.GetId(), aura.GetCasterGUID());
|
||||
if (oldAura != null)
|
||||
Unit unitStealer = stealer.ToUnit();
|
||||
if (unitStealer != null)
|
||||
{
|
||||
if (stealCharge)
|
||||
oldAura.ModCharges(1);
|
||||
else
|
||||
oldAura.ModStackAmount(1);
|
||||
oldAura.SetDuration((int)dur);
|
||||
}
|
||||
else
|
||||
{
|
||||
// single target state must be removed before aura creation to preserve existing single target aura
|
||||
if (aura.IsSingleTarget())
|
||||
aura.UnregisterSingleTarget();
|
||||
|
||||
AuraCreateInfo createInfo = new(aura.GetCastGUID(), aura.GetSpellInfo(), aura.GetCastDifficulty(), effMask, stealer);
|
||||
createInfo.SetCasterGUID(aura.GetCasterGUID());
|
||||
createInfo.SetBaseAmount(baseDamage);
|
||||
|
||||
Aura newAura = Aura.TryRefreshStackOrCreate(createInfo);
|
||||
if (newAura != null)
|
||||
Aura oldAura = unitStealer.GetAura(aura.GetId(), aura.GetCasterGUID());
|
||||
if (oldAura != null)
|
||||
{
|
||||
// created aura must not be single target aura, so stealer won't loose it on recast
|
||||
if (newAura.IsSingleTarget())
|
||||
if (stealCharge)
|
||||
oldAura.ModCharges(1);
|
||||
else
|
||||
oldAura.ModStackAmount(1);
|
||||
oldAura.SetDuration((int)dur);
|
||||
}
|
||||
else
|
||||
{
|
||||
// single target state must be removed before aura creation to preserve existing single target aura
|
||||
if (aura.IsSingleTarget())
|
||||
aura.UnregisterSingleTarget();
|
||||
|
||||
AuraCreateInfo createInfo = new(aura.GetCastGUID(), aura.GetSpellInfo(), aura.GetCastDifficulty(), effMask, stealer);
|
||||
createInfo.SetCasterGUID(aura.GetCasterGUID());
|
||||
createInfo.SetBaseAmount(baseDamage);
|
||||
|
||||
Aura newAura = Aura.TryRefreshStackOrCreate(createInfo);
|
||||
if (newAura != null)
|
||||
{
|
||||
newAura.UnregisterSingleTarget();
|
||||
// bring back single target aura status to the old aura
|
||||
aura.SetIsSingleTarget(true);
|
||||
caster.GetSingleCastAuras().Add(aura);
|
||||
// created aura must not be single target aura, so stealer won't loose it on recast
|
||||
if (newAura.IsSingleTarget())
|
||||
{
|
||||
newAura.UnregisterSingleTarget();
|
||||
// bring back single target aura status to the old aura
|
||||
aura.SetIsSingleTarget(true);
|
||||
caster.GetSingleCastAuras().Add(aura);
|
||||
}
|
||||
// FIXME: using aura.GetMaxDuration() maybe not blizzlike but it fixes stealing of spells like Innervate
|
||||
newAura.SetLoadedState(aura.GetMaxDuration(), (int)dur, stealCharge ? 1 : aura.GetCharges(), 1, recalculateMask, damage);
|
||||
newAura.ApplyForTargets();
|
||||
}
|
||||
// FIXME: using aura.GetMaxDuration() maybe not blizzlike but it fixes stealing of spells like Innervate
|
||||
newAura.SetLoadedState(aura.GetMaxDuration(), (int)dur, stealCharge ? 1 : aura.GetCharges(), 1, recalculateMask, damage);
|
||||
newAura.ApplyForTargets();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3524,7 +3220,7 @@ namespace Game.Entities
|
||||
}
|
||||
}
|
||||
}
|
||||
public void RemoveAurasDueToSpellByDispel(uint spellId, uint dispellerSpellId, ObjectGuid casterGUID, Unit dispeller, byte chargesRemoved = 1)
|
||||
public void RemoveAurasDueToSpellByDispel(uint spellId, uint dispellerSpellId, ObjectGuid casterGUID, WorldObject dispeller, byte chargesRemoved = 1)
|
||||
{
|
||||
foreach (var pair in GetOwnedAuras())
|
||||
{
|
||||
@@ -4270,6 +3966,14 @@ namespace Game.Entities
|
||||
if (createInfo.CasterGUID.IsEmpty() && !createInfo.GetSpellInfo().IsStackableOnOneSlotWithDifferentCasters())
|
||||
createInfo.CasterGUID = createInfo.Caster.GetGUID();
|
||||
|
||||
// world gameobjects can't own auras and they send empty casterguid
|
||||
// checked on sniffs with spell 22247
|
||||
if (createInfo.CasterGUID.IsGameObject())
|
||||
{
|
||||
createInfo.Caster = null;
|
||||
createInfo.CasterGUID.Clear();
|
||||
}
|
||||
|
||||
// passive and Incanter's Absorption and auras with different type can stack with themselves any number of times
|
||||
if (!createInfo.GetSpellInfo().IsMultiSlotAura())
|
||||
{
|
||||
@@ -4612,7 +4316,7 @@ namespace Game.Entities
|
||||
});
|
||||
}
|
||||
|
||||
int GetMaxNegativeAuraModifierByMiscValue(AuraType auratype, int miscValue)
|
||||
public int GetMaxNegativeAuraModifierByMiscValue(AuraType auratype, int miscValue)
|
||||
{
|
||||
return GetMaxNegativeAuraModifier(auratype, aurEff =>
|
||||
{
|
||||
|
||||
@@ -1394,101 +1394,6 @@ namespace Game.Entities
|
||||
SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.ShapeshiftForm), (byte)form);
|
||||
}
|
||||
|
||||
public int CalcSpellDuration(SpellInfo spellProto)
|
||||
{
|
||||
sbyte comboPoints = (sbyte)(m_playerMovingMe != null ? m_playerMovingMe.GetComboPoints() : 0);
|
||||
|
||||
int minduration = spellProto.GetDuration();
|
||||
int maxduration = spellProto.GetMaxDuration();
|
||||
|
||||
int duration;
|
||||
|
||||
if (comboPoints != 0 && minduration != -1 && minduration != maxduration)
|
||||
duration = minduration + (maxduration - minduration) * comboPoints / 5;
|
||||
else
|
||||
duration = minduration;
|
||||
|
||||
return duration;
|
||||
}
|
||||
|
||||
public int ModSpellDuration(SpellInfo spellProto, Unit target, int duration, bool positive, uint effectMask)
|
||||
{
|
||||
// don't mod permanent auras duration
|
||||
if (duration < 0)
|
||||
return duration;
|
||||
|
||||
// some auras are not affected by duration modifiers
|
||||
if (spellProto.HasAttribute(SpellAttr7.IgnoreDurationMods))
|
||||
return duration;
|
||||
|
||||
// cut duration only of negative effects
|
||||
if (!positive)
|
||||
{
|
||||
uint mechanic = spellProto.GetSpellMechanicMaskByEffectMask(effectMask);
|
||||
|
||||
int durationMod;
|
||||
int durationMod_always = 0;
|
||||
int durationMod_not_stack = 0;
|
||||
|
||||
for (byte i = 1; i <= (int)Mechanics.Enraged; ++i)
|
||||
{
|
||||
if (!Convert.ToBoolean(mechanic & 1 << i))
|
||||
continue;
|
||||
// Find total mod value (negative bonus)
|
||||
int new_durationMod_always = target.GetTotalAuraModifierByMiscValue(AuraType.MechanicDurationMod, i);
|
||||
// Find max mod (negative bonus)
|
||||
int new_durationMod_not_stack = target.GetMaxNegativeAuraModifierByMiscValue(AuraType.MechanicDurationModNotStack, i);
|
||||
// Check if mods applied before were weaker
|
||||
if (new_durationMod_always < durationMod_always)
|
||||
durationMod_always = new_durationMod_always;
|
||||
if (new_durationMod_not_stack < durationMod_not_stack)
|
||||
durationMod_not_stack = new_durationMod_not_stack;
|
||||
}
|
||||
|
||||
// Select strongest negative mod
|
||||
if (durationMod_always > durationMod_not_stack)
|
||||
durationMod = durationMod_not_stack;
|
||||
else
|
||||
durationMod = durationMod_always;
|
||||
|
||||
if (durationMod != 0)
|
||||
MathFunctions.AddPct(ref duration, durationMod);
|
||||
|
||||
// there are only negative mods currently
|
||||
durationMod_always = target.GetTotalAuraModifierByMiscValue(AuraType.ModAuraDurationByDispel, (int)spellProto.Dispel);
|
||||
durationMod_not_stack = target.GetMaxNegativeAuraModifierByMiscValue(AuraType.ModAuraDurationByDispelNotStack, (int)spellProto.Dispel);
|
||||
|
||||
durationMod = 0;
|
||||
if (durationMod_always > durationMod_not_stack)
|
||||
durationMod += durationMod_not_stack;
|
||||
else
|
||||
durationMod += durationMod_always;
|
||||
|
||||
if (durationMod != 0)
|
||||
MathFunctions.AddPct(ref duration, durationMod);
|
||||
}
|
||||
else
|
||||
{
|
||||
// else positive mods here, there are no currently
|
||||
// when there will be, change GetTotalAuraModifierByMiscValue to GetTotalPositiveAuraModifierByMiscValue
|
||||
|
||||
// Mixology - duration boost
|
||||
if (target.IsTypeId(TypeId.Player))
|
||||
{
|
||||
if (spellProto.SpellFamilyName == SpellFamilyNames.Potion && (
|
||||
Global.SpellMgr.IsSpellMemberOfSpellGroup(spellProto.Id, SpellGroup.ElixirBattle) ||
|
||||
Global.SpellMgr.IsSpellMemberOfSpellGroup(spellProto.Id, SpellGroup.ElixirGuardian)))
|
||||
{
|
||||
SpellEffectInfo effect = spellProto.GetEffect(0);
|
||||
if (target.HasAura(53042) && effect != null && target.HasSpell(effect.TriggerSpell))
|
||||
duration *= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Math.Max(duration, 0);
|
||||
}
|
||||
|
||||
// creates aura application instance and registers it in lists
|
||||
// aura application effects are handled separately to prevent aura list corruption
|
||||
public AuraApplication _CreateAuraApplication(Aura aura, uint effMask)
|
||||
@@ -1528,8 +1433,8 @@ namespace Game.Entities
|
||||
return aurApp;
|
||||
}
|
||||
|
||||
bool HasInterruptFlag(SpellAuraInterruptFlags flags) { return m_interruptMask.HasFlag(flags); }
|
||||
bool HasInterruptFlag(SpellAuraInterruptFlags2 flags) { return m_interruptMask2.HasFlag(flags); }
|
||||
bool HasInterruptFlag(SpellAuraInterruptFlags flags) { return m_interruptMask.HasAnyFlag(flags); }
|
||||
bool HasInterruptFlag(SpellAuraInterruptFlags2 flags) { return m_interruptMask2.HasAnyFlag(flags); }
|
||||
|
||||
public void AddInterruptMask(SpellAuraInterruptFlags flags, SpellAuraInterruptFlags2 flags2)
|
||||
{
|
||||
@@ -1649,26 +1554,6 @@ namespace Game.Entities
|
||||
RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.Sheathing);
|
||||
}
|
||||
|
||||
public FactionTemplateRecord GetFactionTemplateEntry()
|
||||
{
|
||||
FactionTemplateRecord entry = CliDB.FactionTemplateStorage.LookupByKey(GetFaction());
|
||||
if (entry == null)
|
||||
{
|
||||
Player player = ToPlayer();
|
||||
if (player != null)
|
||||
Log.outError(LogFilter.Unit, "Player {0} has invalid faction (faction template id) #{1}", player.GetName(), GetFaction());
|
||||
else
|
||||
{
|
||||
Creature creature = ToCreature();
|
||||
if (creature != null)
|
||||
Log.outError(LogFilter.Unit, "Creature (template id: {0}) has invalid faction (faction template id) #{1}", creature.GetCreatureTemplate().Entry, GetFaction());
|
||||
else
|
||||
Log.outError(LogFilter.Unit, "Unit (name={0}, type={1}) has invalid faction (faction template id) #{2}", GetName(), GetTypeId(), GetFaction());
|
||||
}
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
public bool IsInFeralForm()
|
||||
{
|
||||
ShapeShiftForm form = GetShapeshiftForm();
|
||||
@@ -1902,17 +1787,9 @@ namespace Game.Entities
|
||||
public uint GetMountDisplayId() { return m_unitData.MountDisplayID; }
|
||||
public void SetMountDisplayId(uint mountDisplayId) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.MountDisplayID), mountDisplayId); }
|
||||
|
||||
public virtual Unit GetOwner()
|
||||
{
|
||||
ObjectGuid ownerid = GetOwnerGUID();
|
||||
if (!ownerid.IsEmpty())
|
||||
return Global.ObjAccessor.GetUnit(this, ownerid);
|
||||
|
||||
return null;
|
||||
}
|
||||
public virtual float GetFollowAngle() { return MathFunctions.PiOver2; }
|
||||
|
||||
public ObjectGuid GetOwnerGUID() { return m_unitData.SummonedBy; }
|
||||
public override ObjectGuid GetOwnerGUID() { return m_unitData.SummonedBy; }
|
||||
public void SetOwnerGUID(ObjectGuid owner)
|
||||
{
|
||||
if (GetOwnerGUID() == owner)
|
||||
@@ -1947,18 +1824,10 @@ namespace Game.Entities
|
||||
public void SetCritterGUID(ObjectGuid guid) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.Critter), guid); }
|
||||
public ObjectGuid GetBattlePetCompanionGUID() { return m_unitData.BattlePetCompanionGUID; }
|
||||
public void SetBattlePetCompanionGUID(ObjectGuid guid) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.BattlePetCompanionGUID), guid); }
|
||||
public ObjectGuid GetCharmerOrOwnerGUID()
|
||||
public override ObjectGuid GetCharmerOrOwnerGUID()
|
||||
{
|
||||
return !GetCharmerGUID().IsEmpty() ? GetCharmerGUID() : GetOwnerGUID();
|
||||
}
|
||||
public ObjectGuid GetCharmerOrOwnerOrOwnGUID()
|
||||
{
|
||||
ObjectGuid guid = GetCharmerOrOwnerGUID();
|
||||
if (!guid.IsEmpty())
|
||||
return guid;
|
||||
|
||||
return GetGUID();
|
||||
}
|
||||
public Unit GetCharmer()
|
||||
{
|
||||
ObjectGuid charmerid = GetCharmerGUID();
|
||||
@@ -1966,22 +1835,6 @@ namespace Game.Entities
|
||||
return Global.ObjAccessor.GetUnit(this, charmerid);
|
||||
return null;
|
||||
}
|
||||
public Unit GetCharmerOrOwnerOrSelf()
|
||||
{
|
||||
Unit u = GetCharmerOrOwner();
|
||||
if (u != null)
|
||||
return u;
|
||||
|
||||
return this;
|
||||
}
|
||||
public Player GetCharmerOrOwnerPlayerOrPlayerItself()
|
||||
{
|
||||
ObjectGuid guid = GetCharmerOrOwnerGUID();
|
||||
if (guid.IsPlayer())
|
||||
return Global.ObjAccessor.FindPlayer(guid);
|
||||
|
||||
return IsTypeId(TypeId.Player) ? ToPlayer() : null;
|
||||
}
|
||||
public Unit GetCharmerOrOwner()
|
||||
{
|
||||
return !GetCharmerGUID().IsEmpty() ? GetCharmer() : GetOwner();
|
||||
@@ -2044,16 +1897,6 @@ namespace Game.Entities
|
||||
else
|
||||
return ToCreature().GetCreatureTemplate().CreatureType;
|
||||
}
|
||||
public Player GetAffectingPlayer()
|
||||
{
|
||||
if (GetCharmerOrOwnerGUID().IsEmpty())
|
||||
return IsTypeId(TypeId.Player) ? ToPlayer() : null;
|
||||
|
||||
Unit owner = GetCharmerOrOwner();
|
||||
if (owner != null)
|
||||
return owner.GetCharmerOrOwnerPlayerOrPlayerItself();
|
||||
return null;
|
||||
}
|
||||
|
||||
public void DeMorph()
|
||||
{
|
||||
@@ -2079,7 +1922,7 @@ namespace Game.Entities
|
||||
}
|
||||
public bool HasUnitState(UnitState f)
|
||||
{
|
||||
return m_state.HasFlag(f);
|
||||
return m_state.HasAnyFlag(f);
|
||||
}
|
||||
public void ClearUnitState(UnitState f)
|
||||
{
|
||||
@@ -2113,146 +1956,8 @@ namespace Game.Entities
|
||||
return false;
|
||||
}
|
||||
|
||||
//Faction
|
||||
public bool IsNeutralToAll()
|
||||
{
|
||||
var my_faction = GetFactionTemplateEntry();
|
||||
if (my_faction == null || my_faction.Faction == 0)
|
||||
return true;
|
||||
|
||||
var raw_faction = CliDB.FactionStorage.LookupByKey(my_faction.Faction);
|
||||
if (raw_faction != null && raw_faction.ReputationIndex >= 0)
|
||||
return false;
|
||||
|
||||
return my_faction.IsNeutralToAll();
|
||||
}
|
||||
public bool IsHostileTo(Unit unit)
|
||||
{
|
||||
return GetReactionTo(unit) <= ReputationRank.Hostile;
|
||||
}
|
||||
public bool IsFriendlyTo(Unit unit)
|
||||
{
|
||||
return GetReactionTo(unit) >= ReputationRank.Friendly;
|
||||
}
|
||||
public ReputationRank GetReactionTo(Unit target)
|
||||
{
|
||||
// always friendly to self
|
||||
if (this == target)
|
||||
return ReputationRank.Friendly;
|
||||
|
||||
// always friendly to charmer or owner
|
||||
if (GetCharmerOrOwnerOrSelf() == target.GetCharmerOrOwnerOrSelf())
|
||||
return ReputationRank.Friendly;
|
||||
|
||||
if (HasUnitFlag(UnitFlags.PvpAttackable))
|
||||
{
|
||||
if (target.HasUnitFlag(UnitFlags.PvpAttackable))
|
||||
{
|
||||
Player selfPlayerOwner = GetAffectingPlayer();
|
||||
Player targetPlayerOwner = target.GetAffectingPlayer();
|
||||
|
||||
if (selfPlayerOwner != null && targetPlayerOwner != null)
|
||||
{
|
||||
// always friendly to other unit controlled by player, or to the player himself
|
||||
if (selfPlayerOwner == targetPlayerOwner)
|
||||
return ReputationRank.Friendly;
|
||||
|
||||
// duel - always hostile to opponent
|
||||
if (selfPlayerOwner.duel != null && selfPlayerOwner.duel.opponent == targetPlayerOwner && selfPlayerOwner.duel.startTime != 0)
|
||||
return ReputationRank.Hostile;
|
||||
|
||||
// same group - checks dependant only on our faction - skip FFA_PVP for example
|
||||
if (selfPlayerOwner.IsInRaidWith(targetPlayerOwner))
|
||||
return ReputationRank.Friendly; // return true to allow config option AllowTwoSide.Interaction.Group to work
|
||||
}
|
||||
|
||||
// check FFA_PVP
|
||||
if (IsFFAPvP() && target.IsFFAPvP())
|
||||
return ReputationRank.Hostile;
|
||||
|
||||
if (selfPlayerOwner != null)
|
||||
{
|
||||
var targetFactionTemplateEntry = target.GetFactionTemplateEntry();
|
||||
if (targetFactionTemplateEntry != null)
|
||||
{
|
||||
if (!selfPlayerOwner.HasUnitFlag2(UnitFlags2.IgnoreReputation))
|
||||
{
|
||||
var targetFactionEntry = CliDB.FactionStorage.LookupByKey(targetFactionTemplateEntry.Faction);
|
||||
if (targetFactionEntry != null)
|
||||
{
|
||||
if (targetFactionEntry.CanHaveReputation())
|
||||
{
|
||||
// check contested flags
|
||||
if (Convert.ToBoolean(targetFactionTemplateEntry.Flags & (uint)FactionTemplateFlags.ContestedGuard)
|
||||
&& selfPlayerOwner.HasPlayerFlag(PlayerFlags.ContestedPVP))
|
||||
return ReputationRank.Hostile;
|
||||
|
||||
// if faction has reputation, hostile state depends only from AtWar state
|
||||
if (selfPlayerOwner.GetReputationMgr().IsAtWar(targetFactionEntry))
|
||||
return ReputationRank.Hostile;
|
||||
return ReputationRank.Friendly;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// do checks dependant only on our faction
|
||||
return GetFactionReactionTo(GetFactionTemplateEntry(), target);
|
||||
}
|
||||
ReputationRank GetFactionReactionTo(FactionTemplateRecord factionTemplateEntry, Unit target)
|
||||
{
|
||||
// always neutral when no template entry found
|
||||
if (factionTemplateEntry == null)
|
||||
return ReputationRank.Neutral;
|
||||
|
||||
var targetFactionTemplateEntry = target.GetFactionTemplateEntry();
|
||||
if (targetFactionTemplateEntry == null)
|
||||
return ReputationRank.Neutral;
|
||||
|
||||
Player targetPlayerOwner = target.GetAffectingPlayer();
|
||||
if (targetPlayerOwner != null)
|
||||
{
|
||||
// check contested flags
|
||||
if (Convert.ToBoolean(factionTemplateEntry.Flags & (uint)FactionTemplateFlags.ContestedGuard)
|
||||
&& targetPlayerOwner.HasPlayerFlag(PlayerFlags.ContestedPVP))
|
||||
return ReputationRank.Hostile;
|
||||
ReputationRank repRank = targetPlayerOwner.GetReputationMgr().GetForcedRankIfAny(factionTemplateEntry);
|
||||
if (repRank != ReputationRank.None)
|
||||
return repRank;
|
||||
if (!target.HasUnitFlag2(UnitFlags2.IgnoreReputation))
|
||||
{
|
||||
var factionEntry = CliDB.FactionStorage.LookupByKey(factionTemplateEntry.Faction);
|
||||
if (factionEntry != null)
|
||||
{
|
||||
if (factionEntry.CanHaveReputation())
|
||||
{
|
||||
// CvP case - check reputation, don't allow state higher than neutral when at war
|
||||
repRank = targetPlayerOwner.GetReputationMgr().GetRank(factionEntry);
|
||||
if (targetPlayerOwner.GetReputationMgr().IsAtWar(factionEntry))
|
||||
repRank = (ReputationRank)Math.Min((int)ReputationRank.Neutral, (int)repRank);
|
||||
return repRank;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// common faction based check
|
||||
if (factionTemplateEntry.IsHostileTo(targetFactionTemplateEntry))
|
||||
return ReputationRank.Hostile;
|
||||
if (factionTemplateEntry.IsFriendlyTo(targetFactionTemplateEntry))
|
||||
return ReputationRank.Friendly;
|
||||
if (targetFactionTemplateEntry.IsFriendlyTo(factionTemplateEntry))
|
||||
return ReputationRank.Friendly;
|
||||
if (Convert.ToBoolean(factionTemplateEntry.Flags & (uint)FactionTemplateFlags.HostileByDefault))
|
||||
return ReputationRank.Hostile;
|
||||
// neutral by default
|
||||
return ReputationRank.Neutral;
|
||||
}
|
||||
|
||||
public uint GetFaction() { return m_unitData.FactionTemplate; }
|
||||
public void SetFaction(uint faction) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.FactionTemplate), faction); }
|
||||
public override uint GetFaction() { return m_unitData.FactionTemplate; }
|
||||
public override void SetFaction(uint faction) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.FactionTemplate), faction); }
|
||||
|
||||
public void RestoreFaction()
|
||||
{
|
||||
@@ -2834,60 +2539,58 @@ namespace Game.Entities
|
||||
}
|
||||
}
|
||||
|
||||
if (damagetype != DamageEffectType.NoDamage)
|
||||
if (damagetype != DamageEffectType.NoDamage && damagetype != DamageEffectType.DOT)
|
||||
{
|
||||
if (victim != attacker && (spellProto == null || !(spellProto.HasAttribute(SpellAttr7.NoPushbackOnDamage) || spellProto.HasAttribute(SpellAttr3.TreatAsPeriodic))))
|
||||
{
|
||||
if (damagetype != DamageEffectType.DOT)
|
||||
Spell spell = victim.GetCurrentSpell(CurrentSpellTypes.Generic);
|
||||
if (spell != null)
|
||||
{
|
||||
Spell spell = victim.GetCurrentSpell(CurrentSpellTypes.Generic);
|
||||
if (spell != null)
|
||||
if (spell.GetState() == SpellState.Preparing)
|
||||
{
|
||||
if (spell.GetState() == SpellState.Preparing)
|
||||
bool isCastInterrupted()
|
||||
{
|
||||
bool isCastInterrupted()
|
||||
{
|
||||
if (damage == 0)
|
||||
return spell.m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.ZeroDamageCancels);
|
||||
if (damage == 0)
|
||||
return spell.m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.ZeroDamageCancels);
|
||||
|
||||
if (victim.IsPlayer() && spell.m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.DamageCancelsPlayerOnly))
|
||||
return true;
|
||||
if (victim.IsPlayer() && spell.m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.DamageCancelsPlayerOnly))
|
||||
return true;
|
||||
|
||||
if (spell.m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.DamageCancels))
|
||||
return true;
|
||||
if (spell.m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.DamageCancels))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
bool isCastDelayed()
|
||||
{
|
||||
if (damage == 0)
|
||||
return false;
|
||||
};
|
||||
|
||||
bool isCastDelayed()
|
||||
{
|
||||
if (damage == 0)
|
||||
return false;
|
||||
if (victim.IsPlayer() && spell.m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.DamagePushbackPlayerOnly))
|
||||
return true;
|
||||
|
||||
if (victim.IsPlayer() && spell.m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.DamagePushbackPlayerOnly))
|
||||
return true;
|
||||
if (spell.m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.DamagePushback))
|
||||
return true;
|
||||
|
||||
if (spell.m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.DamagePushback))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isCastInterrupted())
|
||||
victim.InterruptNonMeleeSpells(false);
|
||||
else if (isCastDelayed())
|
||||
spell.Delayed();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (damage != 0 && victim.IsPlayer())
|
||||
{
|
||||
Spell spell1 = victim.GetCurrentSpell(CurrentSpellTypes.Channeled);
|
||||
if (spell1 != null)
|
||||
if (spell1.GetState() == SpellState.Casting && spell1.m_spellInfo.HasChannelInterruptFlag(SpellAuraInterruptFlags.DamageChannelDuration))
|
||||
spell1.DelayedChannel();
|
||||
if (isCastInterrupted())
|
||||
victim.InterruptNonMeleeSpells(false);
|
||||
else if (isCastDelayed())
|
||||
spell.Delayed();
|
||||
}
|
||||
}
|
||||
|
||||
if (damage != 0 && victim.IsPlayer())
|
||||
{
|
||||
Spell spell1 = victim.GetCurrentSpell(CurrentSpellTypes.Channeled);
|
||||
if (spell1 != null)
|
||||
if (spell1.GetState() == SpellState.Casting && spell1.m_spellInfo.HasChannelInterruptFlag(SpellAuraInterruptFlags.DamageChannelDuration))
|
||||
spell1.DelayedChannel();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3322,6 +3025,47 @@ namespace Game.Entities
|
||||
return nearMembers[randTarget];
|
||||
}
|
||||
|
||||
public uint GetComboPoints() { return (uint)GetPower(PowerType.ComboPoints); }
|
||||
|
||||
public void AddComboPoints(sbyte count, Spell spell = null)
|
||||
{
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
sbyte comboPoints = (sbyte)(spell != null ? spell.m_comboPointGain : GetPower(PowerType.ComboPoints));
|
||||
|
||||
comboPoints += count;
|
||||
|
||||
if (comboPoints > 5)
|
||||
comboPoints = 5;
|
||||
else if (comboPoints < 0)
|
||||
comboPoints = 0;
|
||||
|
||||
if (!spell)
|
||||
SetPower(PowerType.ComboPoints, comboPoints);
|
||||
else
|
||||
spell.m_comboPointGain = comboPoints;
|
||||
}
|
||||
|
||||
void GainSpellComboPoints(sbyte count)
|
||||
{
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
sbyte cp = (sbyte)GetPower(PowerType.ComboPoints);
|
||||
|
||||
cp += count;
|
||||
if (cp > 5) cp = 5;
|
||||
else if (cp < 0) cp = 0;
|
||||
|
||||
SetPower(PowerType.ComboPoints, cp);
|
||||
}
|
||||
|
||||
public void ClearComboPoints()
|
||||
{
|
||||
SetPower(PowerType.ComboPoints, 0);
|
||||
}
|
||||
|
||||
public void ClearAllReactives()
|
||||
{
|
||||
for (ReactiveType i = 0; i < ReactiveType.Max; ++i)
|
||||
@@ -3411,22 +3155,25 @@ namespace Game.Entities
|
||||
return (uint)damageResisted;
|
||||
}
|
||||
|
||||
static float CalculateAverageResistReduction(Unit attacker, SpellSchoolMask schoolMask, Unit victim, SpellInfo spellInfo = null)
|
||||
static float CalculateAverageResistReduction(WorldObject caster, SpellSchoolMask schoolMask, Unit victim, SpellInfo spellInfo = null)
|
||||
{
|
||||
float victimResistance = victim.GetResistance(schoolMask);
|
||||
|
||||
if (attacker != null)
|
||||
if (caster != null)
|
||||
{
|
||||
// pets inherit 100% of masters penetration
|
||||
// excluding traps
|
||||
Player player = attacker.GetSpellModOwner();
|
||||
if (player != null && attacker.GetEntry() != SharedConst.WorldTrigger)
|
||||
Player player = caster.GetSpellModOwner();
|
||||
if (player != null)
|
||||
{
|
||||
victimResistance += player.GetTotalAuraModifierByMiscMask(AuraType.ModTargetResistance, (int)schoolMask);
|
||||
victimResistance -= player.GetSpellPenetrationItemMod();
|
||||
}
|
||||
else
|
||||
victimResistance += attacker.GetTotalAuraModifierByMiscMask(AuraType.ModTargetResistance, (int)schoolMask);
|
||||
{
|
||||
Unit unitCaster = caster.ToUnit();
|
||||
if (unitCaster != null)
|
||||
victimResistance += unitCaster.GetTotalAuraModifierByMiscMask(AuraType.ModTargetResistance, (int)schoolMask);
|
||||
}
|
||||
}
|
||||
|
||||
// holy resistance exists in pve and comes from level difference, ignore template values
|
||||
@@ -3440,12 +3187,13 @@ 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 (attacker != null && (spellInfo == null || !spellInfo.HasAttribute(SpellCustomAttributes.BinarySpell)))
|
||||
victimResistance += Math.Max(((float)victim.GetLevelForTarget(attacker) - (float)attacker.GetLevelForTarget(victim)) * 5.0f, 0.0f);
|
||||
// gameobject caster -- should it have level based resistance?
|
||||
if (caster != null && !caster.IsGameObject() && (spellInfo == null || !spellInfo.HasAttribute(SpellCustomAttributes.BinarySpell)))
|
||||
victimResistance += Math.Max(((float)victim.GetLevelForTarget(caster) - (float)caster.GetLevelForTarget(victim)) * 5.0f, 0.0f);
|
||||
|
||||
uint bossLevel = 83;
|
||||
float bossResistanceConstant = 510.0f;
|
||||
uint level = attacker != null ? victim.GetLevelForTarget(attacker) : attacker.GetLevel();
|
||||
uint level = caster ? victim.GetLevelForTarget(caster) : victim.GetLevel();
|
||||
float resistanceConstant;
|
||||
|
||||
if (level == bossLevel)
|
||||
|
||||
Reference in New Issue
Block a user