PageRenderTime 74ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/Services/WCell.RealmServer/Spells/Spell.Aura.cs

https://github.com/enjoii/WCell
C# | 472 lines | 293 code | 62 blank | 117 comment | 80 complexity | c4f1747e8b2f470960a0d3f6f8c8cda3 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using WCell.Constants.Spells;
  5. using WCell.RealmServer.Entities;
  6. using WCell.RealmServer.Spells.Auras;
  7. using WCell.Util;
  8. using WCell.Util.NLog;
  9. namespace WCell.RealmServer.Spells
  10. {
  11. /// <summary>
  12. /// Aura-related information of a Spell
  13. /// </summary>
  14. public partial class Spell
  15. {
  16. #region Auto generated Aura Fields
  17. /// <summary>
  18. /// Whether this Spell is an Aura
  19. /// </summary>
  20. public bool IsAura;
  21. /// <summary>
  22. /// AuraUID, the same for all Auras that may not stack
  23. /// </summary>
  24. public uint AuraUID;
  25. public bool HasPeriodicAuraEffects;
  26. public bool HasNonPeriodicAuraEffects;
  27. /// <summary>
  28. /// AuraFlags to be applied to all Auras resulting from this Spell
  29. /// </summary>
  30. public AuraFlags DefaultAuraFlags;
  31. /// <summary>
  32. /// General Amplitude for Spells that represent Auras (can only have one for the time being)
  33. /// </summary>
  34. public int AuraAmplitude;
  35. /// <summary>
  36. /// whether this Spell is an AreaAura
  37. /// </summary>
  38. public bool IsAreaAura;
  39. /// <summary>
  40. /// Modal Auras cannot be updated, but must be replaced
  41. /// </summary>
  42. public bool IsAutoRepeating;
  43. /// <summary>
  44. /// General Amplitude for Spells that represent AreaAuras (can only have one per spell)
  45. /// </summary>
  46. public int AreaAuraAmplitude;
  47. /// <summary>
  48. /// All effects that belong to an Aura
  49. /// </summary>
  50. public SpellEffect[] AuraEffects;
  51. /// <summary>
  52. /// All effects that belong to an AreaAura
  53. /// </summary>
  54. public SpellEffect[] AreaAuraEffects;
  55. /// <summary>
  56. /// Whether the Aura's effects should multiply it's effect value by the amount of its Applications
  57. /// </summary>
  58. public bool CanStack;
  59. /// <summary>
  60. /// The amount of initial Aura-Applications
  61. /// </summary>
  62. public int StackCount;
  63. /// <summary>
  64. /// Only has Aura effects
  65. /// </summary>
  66. public bool IsPureAura;
  67. /// <summary>
  68. /// Only has positive Aura effects
  69. /// </summary>
  70. public bool IsPureBuff;
  71. /// <summary>
  72. /// Only has negative Aura effects
  73. /// </summary>
  74. public bool IsPureDebuff;
  75. /// <summary>
  76. /// whether this Spell applies the death effect
  77. /// </summary>
  78. public bool IsGhost;
  79. /// <summary>
  80. /// Whether this is a proc and whether its own effects handle procs (or false, if not a proc or custom proc handlers have been added)
  81. /// </summary>
  82. public bool IsAuraProcHandler
  83. {
  84. get { return IsProc && ProcHandlers == null; }
  85. }
  86. public bool IsVehicle;
  87. /// <summary>
  88. /// Spell lets one shapeshift into another creature
  89. /// </summary>
  90. public bool IsShapeshift;
  91. /// <summary>
  92. /// whether this spell applies makes the targets fly
  93. /// </summary>
  94. public bool HasFlyEffect;
  95. /// <summary>
  96. /// Does this Spell apply a Mount-Aura?
  97. /// </summary>
  98. public bool IsMount
  99. {
  100. get { return Mechanic == SpellMechanic.Mounted; }
  101. }
  102. /// <summary>
  103. /// Does this Spell apply a Flying-Mount Aura?
  104. /// </summary>
  105. public bool IsFlyingMount;
  106. /// <summary>
  107. ///
  108. /// </summary>
  109. public bool CanApplyMultipleTimes;
  110. /// <summary>
  111. /// Whether the Aura has effects that depend on the wearer's Shapeshift form
  112. /// </summary>
  113. public bool HasShapeshiftDependentEffects;
  114. /// <summary>
  115. /// Whether the Aura is in any way dependent on the wearer's shapeshift form
  116. /// </summary>
  117. public bool IsModalShapeshiftDependentAura;
  118. /// <summary>
  119. ///
  120. /// </summary>
  121. public AuraCasterGroup AuraCasterGroup;
  122. #endregion
  123. #region InitAura
  124. private void InitAura()
  125. {
  126. // procs
  127. if (ProcTriggerFlags != ProcTriggerFlags.None || CasterProcSpells != null)
  128. {
  129. ProcTriggerEffects = Effects.Where(effect => effect.IsProc).ToArray();
  130. if (ProcTriggerEffects.Length == 0)
  131. {
  132. // no proc-specific effects -> all effects are triggered on proc
  133. ProcTriggerEffects = null;
  134. }
  135. //else if (ProcTriggerEffects.Length > 1)
  136. //{
  137. // log.Warn("Spell {0} had more than one ProcTriggerEffect", this);
  138. //}
  139. if (ProcTriggerFlags == (ProcTriggerFlags.MeleeHitOther | ProcTriggerFlags.SpellCast))
  140. {
  141. // we don't want any SpellCast to trigger on that
  142. ProcTriggerFlags = ProcTriggerFlags.MeleeHitOther;
  143. }
  144. IsProc = ProcTriggerEffects != null || ProcHandlers != null || CasterProcSpells != null ||
  145. ProcCharges > 0;
  146. }
  147. IsAura = IsProc || HasEffectWith(effect =>
  148. {
  149. if (effect.AuraType != AuraType.None)
  150. {
  151. return true;
  152. }
  153. return false;
  154. });
  155. ForeachEffect(effect =>
  156. {
  157. if (effect.IsAuraEffect)
  158. {
  159. HasNonPeriodicAuraEffects = HasNonPeriodicAuraEffects || !effect.IsPeriodic;
  160. HasPeriodicAuraEffects = HasPeriodicAuraEffects || effect.IsPeriodic;
  161. }
  162. });
  163. IsAutoRepeating = AttributesExB.HasFlag(SpellAttributesExB.AutoRepeat);
  164. HasManaShield = HasEffectWith(effect => effect.AuraType == AuraType.ManaShield);
  165. AuraEffects = GetEffectsWhere(effect => effect.AuraEffectHandlerCreator != null);
  166. AreaAuraEffects = GetEffectsWhere(effect => effect.IsAreaAuraEffect);
  167. IsAreaAura = AreaAuraEffects != null;
  168. IsPureAura = !IsDamageSpell && !HasEffectWith(effect => effect.EffectType != SpellEffectType.ApplyAura ||
  169. effect.EffectType != SpellEffectType.ApplyAuraToMaster ||
  170. effect.EffectType != SpellEffectType.ApplyStatAura ||
  171. effect.EffectType != SpellEffectType.ApplyStatAuraPercent);
  172. IsPureBuff = IsPureAura && HasBeneficialEffects && !HasHarmfulEffects;
  173. IsPureDebuff = IsPureAura && HasHarmfulEffects && !HasBeneficialEffects;
  174. IsVehicle = HasEffectWith(effect => effect.AuraType == AuraType.Vehicle);
  175. IsShapeshift = HasEffectWith(effect =>
  176. {
  177. //if (effect.AuraType == AuraType.ModShapeshift)
  178. //{
  179. // var info = SpellHandler.ShapeshiftEntries.Get((uint)effect.MiscValue);
  180. // return info.CreatureType > 0;
  181. //}
  182. return effect.AuraType == AuraType.ModShapeshift || effect.AuraType == AuraType.Transform;
  183. });
  184. // charges and stacks:
  185. CanStack = MaxStackCount > 0;
  186. if (ProcCharges > 0)
  187. {
  188. // applications will be used up by procs
  189. StackCount = ProcCharges;
  190. }
  191. else
  192. {
  193. // applications can be added by re-applying
  194. StackCount = 1;
  195. }
  196. IsGhost = HasEffectWith(effect => effect.AuraType == AuraType.Ghost);
  197. HasFlyEffect = HasEffectWith(effect => effect.AuraType == AuraType.Fly);
  198. IsFlyingMount = IsMount &&
  199. HasEffectWith(effect => effect.AuraType == AuraType.ModSpeedMountedFlight);
  200. CanApplyMultipleTimes = Attributes == (SpellAttributes.InvisibleAura | SpellAttributes.Passive) &&
  201. Ability == null && Talent == null;
  202. HasShapeshiftDependentEffects = HasEffectWith(effect => effect.RequiredShapeshiftMask != 0);
  203. IsModalShapeshiftDependentAura = IsPassive && (RequiredShapeshiftMask != 0 || HasShapeshiftDependentEffects);
  204. if (AuraUID == 0)
  205. {
  206. CreateAuraUID();
  207. }
  208. }
  209. #endregion
  210. #region AuraUID Evaluation
  211. private void CreateAuraUID()
  212. {
  213. var count = AuraHandler.AuraIdEvaluators.Count;
  214. //for (var i = count-1; i >= 0; i--)
  215. for (var i = 0u; i < count; i++)
  216. {
  217. var eval = AuraHandler.AuraIdEvaluators[(int)i];
  218. if (eval(this))
  219. {
  220. AuraUID = (uint)SpellLineId.End + i;
  221. break;
  222. }
  223. }
  224. if (AuraUID == 0)
  225. {
  226. // by default the uid is the id of all spells in one line
  227. // and single spells get single unique ids
  228. if (Line != null)
  229. {
  230. AuraUID = Line.AuraUID;
  231. }
  232. else
  233. {
  234. AuraUID = AuraHandler.GetNextAuraUID();
  235. }
  236. }
  237. }
  238. #endregion
  239. #region Caster Proc Spells
  240. /// <summary>
  241. /// Add Spells which, when casted by the owner of this Aura, can cause it to trigger this spell's procs.
  242. /// Don't add damage spells (they will generate a Proc event anyway).
  243. /// </summary>
  244. public void AddCasterProcSpells(params SpellId[] spellIds)
  245. {
  246. var spells = new Spell[spellIds.Length];
  247. for (var i = 0; i < spellIds.Length; i++)
  248. {
  249. var id = spellIds[i];
  250. var spell = SpellHandler.Get(id);
  251. if (spell == null)
  252. {
  253. throw new InvalidSpellDataException("Invalid SpellId: " + id);
  254. }
  255. spells[i] = spell;
  256. }
  257. AddCasterProcSpells(spells);
  258. }
  259. /// <summary>
  260. /// Add Spells which, when casted by the owner of this Aura, can cause it to trigger this spell's procs.
  261. /// Don't add damage spells (they will generate a Proc event anyway).
  262. /// </summary>
  263. public void AddCasterProcSpells(params SpellLineId[] spellSetIds)
  264. {
  265. var list = new List<Spell>(spellSetIds.Length * 6);
  266. foreach (var id in spellSetIds)
  267. {
  268. var line = SpellLines.GetLine(id);
  269. list.AddRange(line);
  270. }
  271. AddCasterProcSpells(list.ToArray());
  272. }
  273. /// <summary>
  274. /// Add Spells which, when casted by the owner of this Aura, can cause it to trigger this spell's procs.
  275. /// Don't add damage spells (they will generate a Proc event anyway).
  276. /// </summary>
  277. public void AddCasterProcSpells(params Spell[] spells)
  278. {
  279. if (CasterProcSpells == null)
  280. {
  281. CasterProcSpells = new HashSet<Spell>();
  282. }
  283. foreach (var spell in spells)
  284. {
  285. spell.GeneratesProcEventOnCast = true;
  286. }
  287. CasterProcSpells.AddRange(spells);
  288. ProcTriggerFlags = ProcTriggerFlags.SpellCast;
  289. }
  290. #endregion
  291. #region Target Proc Spells
  292. /// <summary>
  293. /// Add Spells which, when casted by others on the owner of this Aura, can cause it to trigger it's procs.
  294. /// Don't add damage spells (they will generate a Proc event anyway).
  295. /// </summary>
  296. public void AddTargetProcSpells(params SpellId[] spellIds)
  297. {
  298. var spells = new Spell[spellIds.Length];
  299. for (var i = 0; i < spellIds.Length; i++)
  300. {
  301. var id = spellIds[i];
  302. var spell = SpellHandler.Get(id);
  303. if (spell == null)
  304. {
  305. throw new InvalidSpellDataException("Invalid SpellId: " + id);
  306. }
  307. spells[i] = spell;
  308. }
  309. AddTargetProcSpells(spells);
  310. }
  311. /// <summary>
  312. /// Add Spells which, when casted by others on the owner of this Aura, can cause it to trigger it's procs
  313. /// Don't add damage spells (they will generate a Proc event anyway).
  314. /// </summary>
  315. public void AddTargetProcSpells(params SpellLineId[] spellSetIds)
  316. {
  317. var list = new List<Spell>(spellSetIds.Length * 6);
  318. foreach (var id in spellSetIds)
  319. {
  320. var line = SpellLines.GetLine(id);
  321. list.AddRange(line);
  322. }
  323. AddTargetProcSpells(list.ToArray());
  324. }
  325. /// <summary>
  326. /// Add Spells which, when casted by others on the owner of this Aura, can cause it to trigger it's procs
  327. /// Don't add damage spells (they will generate a Proc event anyway).
  328. /// </summary>
  329. public void AddTargetProcSpells(params Spell[] spells)
  330. {
  331. if (TargetProcSpells == null)
  332. {
  333. TargetProcSpells = new HashSet<Spell>();
  334. }
  335. foreach (var spell in spells)
  336. {
  337. spell.GeneratesProcEventOnCast = true;
  338. }
  339. TargetProcSpells.AddRange(spells);
  340. ProcTriggerFlags = ProcTriggerFlags.SpellCast;
  341. }
  342. #endregion
  343. #region CreateAuraEffectHandlers
  344. public List<AuraEffectHandler> CreateAuraEffectHandlers(
  345. ObjectReference caster,
  346. Unit target,
  347. bool beneficial)
  348. {
  349. return CreateAuraEffectHandlers(AuraEffects, caster, target, beneficial);
  350. }
  351. public static List<AuraEffectHandler> CreateAuraEffectHandlers(SpellEffect[] effects, ObjectReference caster,
  352. Unit target, bool beneficial)
  353. {
  354. if (effects == null)
  355. return null;
  356. try
  357. {
  358. List<AuraEffectHandler> effectHandlers = null;
  359. var failReason = SpellFailedReason.Ok;
  360. for (var i = 0; i < effects.Length; i++)
  361. {
  362. var effect = effects[i];
  363. if (effect.HarmType == HarmType.Beneficial || !beneficial)
  364. {
  365. var effectHandler = effect.CreateAuraEffectHandler(caster, target, ref failReason);
  366. if (failReason != SpellFailedReason.Ok)
  367. {
  368. return null;
  369. }
  370. if (effectHandlers == null)
  371. {
  372. effectHandlers = new List<AuraEffectHandler>(3);
  373. }
  374. effectHandlers.Add(effectHandler);
  375. }
  376. }
  377. return effectHandlers;
  378. }
  379. catch (Exception e)
  380. {
  381. LogUtil.ErrorException(e, "Failed to create AuraEffectHandlers for: " + effects.GetWhere(effect => effect != null).Spell);
  382. return null;
  383. }
  384. }
  385. #endregion
  386. public bool CanOverride(Spell spell)
  387. {
  388. if (CanOverrideEqualAuraRank)
  389. {
  390. return Rank >= spell.Rank;
  391. }
  392. else
  393. {
  394. return Rank > spell.Rank;
  395. }
  396. }
  397. public AuraIndexId GetAuraUID(ObjectReference caster, WorldObject target)
  398. {
  399. return GetAuraUID(IsBeneficialFor(caster, target));
  400. }
  401. public AuraIndexId GetAuraUID(bool positive)
  402. {
  403. return new AuraIndexId
  404. {
  405. AuraUID = !CanApplyMultipleTimes ? AuraUID : AuraHandler.lastAuraUid + ++AuraHandler.randomAuraId,
  406. IsPositive = positive
  407. };
  408. }
  409. }
  410. }