Core/Spells: rework part 5: GameObject casting

Port From (https://github.com/TrinityCore/TrinityCore/commit/962f6d7988b9003e550f6745be7cff812e9d8efa)
This commit is contained in:
hondacrx
2021-08-29 18:27:08 -04:00
parent e97ffa304d
commit 949d4806c5
39 changed files with 5236 additions and 4781 deletions
-328
View File
@@ -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)
+5 -24
View File
@@ -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);
}
}
}
+55 -351
View File
@@ -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 =>
{
+96 -348
View File
@@ -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)