/// <summary> /// Since spells in the same spell line share emotes, the buff tracker can't be sure which one actually landed based on the emote. /// Instead we just normalize the name so that they are all consistent. /// </summary> private string FixName(string name) { //return SpellParser.StripRank(name.Replace("Group ", "")); return(SpellParser.StripRank(name)); }
public BuffTracker(ISpellLookup spells) { Spells = spells; // we only need to add rank 1 of most spells lines because... // -group spells: they the use same emote // -self spells with ranks: we match with any spell name minus the rank // the exception is... // -self spells with different names across levels // specials AddSpell("Glyph of Destruction I"); AddSpell("Glyph of Dragon Scales I"); AddSpell("Intensity of the Resolute"); // archtype AddSpell("Improved Twincast I"); // aa, clr, dru, wiz, mag, enc AddSpell("Killing Spree I"); // war, mnk, rog, ber killshot proc (don't think it gets logged) // bard AddSpell("Spirit of Vesagran"); // epic AddSpell("Fierce Eye I"); AddSpell("Quick Time I"); AddSpell("Dance of Blades I"); // beastlord AddSpell("Group Bestial Alignment I"); // aa, self/group emotes are the same AddSpell("Bestial Alignment I"); // aa AddSpell("Bloodlust I"); // aa AddSpell("Merciless Ferocity"); // L97, T9, single AddSpell("Ruaabri's Fury"); // L98, T4, group AddSpell("Savage Fury"); // L94, T3 AddSpell("Savage Rage"); // L99, T3 AddSpell("Savage Rancor"); // L104, T3 AddSpell("Taste of Blood I"); // killshot proc // berserker AddSpell("Frenzied Resolve Discipline"); AddSpell("Cleaving Rage Discipline"); // L54, T4, probably not used AddSpell("Blind Rage Discipline"); // L58, T4 AddSpell("Berserking Discipline"); // L75, T4 AddSpell("Sundering Discipline"); // L95, T4 AddSpell("Brutal Discipline"); // L100, T4 AddSpell("Cry Havoc"); AddSpell("Battle Cry of the Mastruq"); // cleric AddSpell("Flurry of Life I"); // aa AddSpell("Healing Frenzy I"); // aa // druid AddSpell("Group Spirit of the Great Wolf I"); // aa, self/group emotes are the same AddSpell("Spirit of the Great Wolf I"); AddSpell("Spire of Nature I"); // aa // enchanter AddSpell("Illusions of Grandeur I"); // aa AddSpell("Chromatic Haze I"); // aa // magician AddSpell("Frenzied Burnout I"); // aa, pet AddSpell("Heart of Skyfire I"); // aa AddSpell("Spire of the Elements I"); // aa // monk // https://forums.daybreakgames.com/eq/index.php?threads/monk-burns.242877/ AddSpell("Ashenhand Discipline"); // L60, T4 AddSpell("Scaledfist Discipline"); // L74, T4 AddSpell("Ironfist Discipline"); // L88, T4 AddSpell("Stonestance Discipline"); // L51, T2 (mitigation) AddSpell("Earthwalk Discipline"); // L65, T2 AddSpell("Impenetrable Discipline"); // L72, T2 AddSpell("Whirlwind Discipline"); // L53, T2 (riposte) AddSpell("Voiddance Discipline"); // L54, T2 (avoidance) AddSpell("Earthforce Discipline"); // L96, T2 AddSpell("Speed Focus Discipline"); // T3 (weapon delay) AddSpell("Innerflame Discipline"); // L57, T3 AddSpell("Crystalpalm Discipline"); // L79, T3 AddSpell("Diamondpalm Discipline"); // L90, T3 AddSpell("Terrorpalm Discipline"); // L99, T3 AddSpell("Hundred Fists Discipline"); // L57, T3 AddSpell("Rapid Kick Discipline"); // L70, T6 AddSpell("Heel of Kanji"); // L70, T6 AddSpell("Heel of Kai"); // L90, T6 AddSpell("Heel of Kojai"); // L95, T6 AddSpell("Heel of Zagali"); // L100, T6 AddSpell("Zan Fi's Whistle I"); // aa AddSpell("Destructive Force I"); // aa AddSpell("Focused Destructive Force I"); // aa AddSpell("Infusion of Thunder I"); // aa AddSpell("Spire of the Sensei I"); // aa // necromancer AddSpell("Curse of Muram"); // anguish robe AddSpell("Spire of Necromancy I"); AddSpell("Hand of Death I"); AddSpell("Heretic's Twincast I"); AddSpell("Funeral Pyre I"); // paladin AddSpell("Pureforge Discipline"); AddSpell("Holyforge Discipline"); AddSpell("Blessing of the Faithful I"); // killshot proc // ranger AddSpell("Auspice of the Hunter I"); // aa AddSpell("Scarlet Cheetah Fang I"); // aa AddSpell("Group Guardian of the Forest I"); // aa, self/group emotes are the same AddSpell("Guardian of the Forest I"); AddSpell("Empowered Blades I"); // aa, same emote as Acromancy and Valorous Rage AddSpell("Outrider's Accuracy I"); // aa AddSpell("Bosquestalker's Discipline"); // L100, T2 (melee) AddSpell("Copsestalker's Discipline"); // L105, T2 AddSpell("Wildstalker's Discipline"); // L110, T2 AddSpell("Arbor Stalker's Discipline"); // L115, T2 AddSpell("Dusksage Stalker's Discipline"); // L120, T2 AddSpell("Trueshot Discipline"); // L55, T2 (archery) AddSpell("Aimshot Discipline"); // L80, T2 AddSpell("Sureshot Discipline"); // L85, T2 AddSpell("Bullseye Discipline"); // L90, T2 AddSpell("Pureshot Discipline"); // L100, T2 AddSpell("Imbued Ferocity I"); // aa AddSpell("Hunter's Fury I"); // aa // rogue // https://forums.daybreakgames.com/eq/index.php?threads/sneak-attack-rogue-disciplines.243116/#post-3983165 AddSpell("Deceiver's Blight"); // epic AddSpell("Frenzied Stabbing Discipline"); AddSpell("Knifeplay Discipline"); // L98, T16 AddSpell("Blinding Speed Discipline"); // L58, T4 AddSpell("Shrouding Speed Discipline"); // L102, T4 AddSpell("Cloaking Speed Discipline"); // L112, T4 AddSpell("Twisted Chance Discipline"); // L65, T4 AddSpell("Rogue's Fury I"); AddSpell("Dissident Weapons"); //AddSpell("Thief's Vengeance"); // L52, T9 (one hit only) AddSpell("Rake's Rampage I"); AddSpell("Rake's Focused Rampage I"); AddSpell("Spire of the Rake I"); // aa // shaman AddSpell("Prophet's Gift of the Ruchu"); // epic AddSpell("Spire of Ancestors I"); // shadowknight AddSpell("Lich Sting"); // epic/group AddSpell("Unholy Aura Discipline"); AddSpell("Visage of Death I"); // aa AddSpell("Visage of Decay I"); // aa AddSpell("Spire of the Reavers I"); // aa AddSpell("Mortal Coil I"); // killshot proc // warrior AddSpell("Heroic Rage I"); // aa AddSpell("Savage Onslaught Discipline"); // L68, T6 AddSpell("Brutal Onslaught Discipline"); // L74, T6 AddSpell("Brightfeld's Onslaught Discipline"); // L117, T6 AddSpell("Mighty Strike Discipline"); // T4 AddSpell("Fellstrike Discipline"); // T4 AddSpell("Defensive Discipline"); // L55, T2 AddSpell("Stonewall Discipline"); // L65, T2 AddSpell("Final Stand Discipline"); // L72, T2 AddSpell("Last Stand Discipline"); // L98, T2 AddSpell("Culminating Stand Discipline"); // L108, T2 AddSpell("Ultimate Stand Discipline"); // L113, T2 AddSpell("Resolute Stand"); // L118, T2 AddSpell("Evasive Discipline"); // T2 AddSpell("Offensive Discipline"); // L97, T2 AddSpell("Furious Discipline"); // L56, T3 AddSpell("Fortitude Discipline"); // L59, T3 // wizard AddSpell("Frenzied Devastation I"); AddSpell("Arcane Fury I"); AddSpell("Arcane Destruction I"); AddSpell("Arcane Overkill I"); // killshot proc AddSpell("Fury of the Gods I"); TrackedSpellsByName = TrackedSpells.ToDictionary(x => SpellParser.StripRank(x.Name)); }
public void HandleEvent(LogEvent e) { // refresh the lookup cache if needed if (TrackedSpellsByName.Count == 0) { TrackedSpellsByName = TrackedSpells.ToDictionary(x => SpellParser.StripRank(x.Name)); } if (e is LogDeathEvent death) { // disabling this because it's still useful to track buffs before death //Buffs.RemoveAll(x => x.CharName == death.Name); // death is just a debuff Buffs.Add(new BuffInfo { Target = death.Name, SpellName = DEATH, Timestamp = death.Timestamp }); } if (e is LogCastingEvent cast) { // casting can be used to safely track self-only buffs since they will only land on the caster // this is better than tracking by emote since we can determine exactly which spell was cast (unless interrupted) var name = SpellParser.StripRank(cast.Spell); TrackedSpellsByName.TryGetValue(name, out SpellInfo spell); if (spell?.Target == (int)SpellTarget.Self || spell?.Target == (int)SpellTarget.Pet) { var buff = new BuffInfo() { Timestamp = e.Timestamp, Target = cast.Source, SpellName = cast.Spell, DurationTicks = spell.DurationTicks }; Buffs.Add(buff); } // if this spell isn't a buff then last cast will be null LastCast[cast.Source] = spell; } if (e is LogShieldEvent shield) { Buffs.Add(new BuffInfo { Target = shield.Target, SpellName = "*Shielded by " + shield.Source, Timestamp = shield.Timestamp }); } if (e is LogRescuedEvent rescue) { Buffs.Add(new BuffInfo { Target = rescue.Target, SpellName = "*Rescued by DI", Timestamp = rescue.Timestamp }); } if (e is LogRawEvent raw) { // strip name from start of string to compare LandsOther version of emote // todo: this doesn't handle names with spaces properly (mob/merc/pet) var otherText = Regex.Replace(raw.Text, @"^\w+", ""); var otherTarget = raw.Text.Substring(0, raw.Text.Length - otherText.Length); foreach (var spell in TrackedSpells) { // lands on self? if (spell.LandSelf == raw.Text && spell.Target != (int)SpellTarget.Self) { // group spells may share an emote with a self spell -- which already get captured during the casting handler // so make sure we aren't double counting LastCast.TryGetValue(raw.Player, out SpellInfo last); if (last?.LandSelf == spell.LandSelf && last?.Target == (int)SpellTarget.Self) { break; } var buff = new BuffInfo() { Timestamp = e.Timestamp, Target = raw.Player, // all spells in a set use the same emote we won't actually know which rank landed SpellName = FixName(spell.Name), DurationTicks = spell.DurationTicks }; Buffs.Add(buff); break; } // if this is a self-only spell then we can discard comparisons with spells the player can't cast //if (spell.Target == (int)SpellTarget.Self && Chars.Get(target)?.Class != spell.ClassesNames) // continue; // lands on others? if (spell.LandOthers == otherText && spell.Target != (int)SpellTarget.Self) { // group spells may share an emote with a self spell -- which already get captured during the casting handler // so make sure we aren't double counting LastCast.TryGetValue(otherTarget, out SpellInfo last); if (last?.LandOthers == spell.LandOthers && last?.Target == (int)SpellTarget.Self) { break; } var buff = new BuffInfo() { Timestamp = e.Timestamp, Target = otherTarget, // all spells in a set use the same emote we won't actually know which rank landed // but don't modify custom effects like "Rescued by DI" SpellName = spell.Name.StartsWith("*") ? spell.Name : FixName(spell.Name), DurationTicks = spell.DurationTicks, }; Buffs.Add(buff); break; } } } }