private static void HandleCombatLog(object sender, LuaEventArgs args) { // Since we hooked this in ctor, make sure we are the selected CC if (RoutineManager.Current.Name != SingularRoutine.Instance.Name) { return; } // convert args to usable form var e = new CombatLogEventArgs(args.EventName, args.FireTimeStamp, args.Args); bool itWasDamage = false; if (TrackDamage || SingularRoutine.CurrentWoWContext == WoWContext.Normal) { if (e.DestGuid == StyxWoW.Me.Guid && e.SourceGuid != StyxWoW.Me.Guid) { long damageAmount = 0; switch (e.EventName) { case "SWING_DAMAGE": itWasDamage = true; damageAmount = (long)e.Args[11]; Logger.WriteDebug("HandleCombatLog(Damage): {0} = {1}", e.EventName, damageAmount); break; case "SPELL_DAMAGE": case "SPELL_PERIODIC_DAMAGE": case "RANGE_DAMAGE": itWasDamage = true; damageAmount = (long)e.Args[14]; break; } if (TrackDamage) { if (itWasDamage) { Logger.WriteDebug("HandleCombatLog(Damage): {0} = {1}", e.EventName, damageAmount); } else { LogUndesirableEvent("On Character", e); } if (damageAmount > 0) { DamageHistory.Enqueue(new Damage(DateTime.UtcNow, damageAmount)); } } if (itWasDamage && SingularRoutine.CurrentWoWContext == WoWContext.Normal) { WoWUnit enemy = e.SourceUnit; if (Unit.ValidUnit(enemy) && enemy.IsPlayer) { Logger.WriteDiagnostic("GankDetect: received {0} src={1} dst={2}", args.EventName, e.SourceGuid, e.DestGuid); // if (guidLastEnemy != enemy.Guid || (TimeLastAttackedByEnemyPlayer - DateTime.UtcNow).TotalSeconds > 30) { guidLastEnemy = enemy.Guid; string extra = ""; if (e.Args.GetUpperBound(0) >= 12) { extra = string.Format(" using {0}", e.SpellName); } AttackedWithSpellSchool = WoWSpellSchool.None; if (e.Args.GetUpperBound(0) >= 12) { AttackedWithSpellSchool = e.SpellSchool; } Logger.WriteDiagnostic("GankDetect: attacked by Level {0} {1}{2}", enemy.Level, enemy.SafeName(), extra); if (SingularSettings.Instance.TargetWorldPvpRegardless && (BotPoi.Current == null || BotPoi.Current.Guid != enemy.Guid)) { Logger.Write(LogColor.Hilite, "GankDetect: setting {0} as BotPoi Kill Target", enemy.SafeName()); BotPoi.Current = new BotPoi(enemy, PoiType.Kill); } } AttackingEnemyPlayer = enemy; TimeLastAttackedByEnemyPlayer = DateTime.UtcNow; } } } } // Logger.WriteDebug("[CombatLog] " + e.Event + " - " + e.SourceName + " - " + e.SpellName); switch (e.Event) { default: LogUndesirableEvent("From Character", e); break; // spell_cast_failed only passes filter in Singular debug mode case "SPELL_CAST_FAILED": Logger.WriteDiagnostic("[CombatLog] {0} {1}#{2} failure: '{3}'", e.Event, e.Spell.Name, e.SpellId, e.Args[14]); if (e.Args[14].ToString() == LocalizedLineOfSightFailure) { WoWGuid guid = WoWGuid.Empty; try { LastLineOfSightTarget = e.DestUnit; guid = LastLineOfSightTarget == null ? WoWGuid.Empty : LastLineOfSightTarget.Guid; } catch { } if (!guid.IsValid) { Logger.WriteFile("[CombatLog] no valid destunit so using CurrentTarget"); LastLineOfSightTarget = StyxWoW.Me.CurrentTarget; guid = StyxWoW.Me.CurrentTargetGuid; } LastLineOfSightFailure = DateTime.UtcNow; Logger.WriteFile("[CombatLog] cast failed due to los reported at {0} on target {1:X}", LastLineOfSightFailure.ToString("HH:mm:ss.fff"), e.DestGuid); } else if (e.Args[14].ToString() == LocalizedUnitNotInfrontFailure) { WoWGuid guid = e.DestGuid; LastUnitNotInfrontFailure = DateTime.UtcNow; if (guid.IsValid && guid != WoWGuid.Empty) { LastUnitNotInfrontGuid = guid; Logger.WriteFile("[CombatLog] not facing SpellTarget [{0}] at {1}", LastUnitNotInfrontGuid, LastUnitNotInfrontFailure.ToString("HH:mm:ss.fff")); } else { LastUnitNotInfrontGuid = Spell.LastSpellTarget; Logger.WriteFile("[CombatLog] not facing LastTarget [{0}] at {1}", LastUnitNotInfrontGuid, LastUnitNotInfrontFailure.ToString("HH:mm:ss.fff"), guid); } } else if (!MovementManager.IsMovementDisabled && StyxWoW.Me.Class == WoWClass.Warrior && e.Args[14].ToString() == LocalizedNoPathAvailableFailure) { LastNoPathFailure = DateTime.UtcNow; LastNoPathGuid = StyxWoW.Me.CurrentTargetGuid; if (!StyxWoW.Me.GotTarget()) { Logger.WriteFile("[CombatLog] cast failed - no path available to current target"); } else { Logger.WriteFile("[CombatLog] cast failed - no path available to {0}, heightOffGround={1}, pos={2}", StyxWoW.Me.CurrentTarget.SafeName(), StyxWoW.Me.CurrentTarget.HeightOffTheGround(), StyxWoW.Me.CurrentTarget.Location ); } } else if (!SingularRoutine.IsManualMovementBotActive && (StyxWoW.Me.Class == WoWClass.Druid || StyxWoW.Me.Class == WoWClass.Shaman)) { if (LocalizedShapeshiftMessages.ContainsKey(e.Args[14].ToString())) { string symbolicName = LocalizedShapeshiftMessages[e.Args[14].ToString()]; SuppressShapeshiftUntil = DateTime.UtcNow.Add(TimeSpan.FromSeconds(30)); Logger.Write(LogColor.Cancel, "/cancel{0} - due to Shapeshift Error '{1}' on cast, suppress form for {2:F1} seconds", StyxWoW.Me.Shapeshift.ToString().CamelToSpaced(), symbolicName, (SuppressShapeshiftUntil - DateTime.UtcNow).TotalSeconds); Lua.DoString("CancelShapeshiftForm()"); } } else if (StyxWoW.Me.Class == WoWClass.Rogue && SingularSettings.Instance.Rogue().UsePickPocket) { if (e.Args[14].ToString() == LocalizedNoPocketsToPickFailure) { HandleRogueNoPocketsError(); } } break; case "SPELL_AURA_APPLIED": case "SPELL_CAST_SUCCESS": if (e.SourceGuid != StyxWoW.Me.Guid) { return; } // Update the last spell we cast. So certain classes can 'switch' their logic around. Spell.LastSpellCast = e.SpellName; Logger.WriteDebug("Storing {0} as last spell cast.", Spell.LastSpellCast); // following commented block should not be needed since rewrite of Pet summon // //// Force a wait for all summoned minions. This prevents double-casting it. //if (StyxWoW.Me.Class == WoWClass.Warlock && e.SpellName.StartsWith("Summon ")) //{ // StyxWoW.SleepForLagDuration(); //} break; case "SWING_MISSED": if (e.Args[11].ToString() == "EVADE") { HandleEvadeBuggedMob(args, e); } else if (e.Args[11].ToString() == "IMMUNE") { WoWUnit unit = e.DestUnit; if (unit != null && !unit.IsPlayer) { Logger.WriteDebug("{0} is immune to Physical spell school", unit.Name); SpellImmunityManager.Add(unit.Entry, WoWSpellSchool.Physical, e.Spell); } } break; case "SPELL_MISSED": case "RANGE_MISSED": // Why log misses? Because users of classes with DoTs testing on training dummies // .. that they don't have enough +Hit for will get DoT spam. This allows easy // .. diagnosis of false reports of rotation issues where a user simply isn't geared // .. this happens more at the beginning of an expansion especially if (SingularSettings.Debug) { Logger.WriteDebug( "[CombatLog] {0} {1}#{2} {3}", e.Event, e.Spell.Name, e.SpellId, e.Args[14] ); } if (e.Args[14].ToString() == "EVADE") { HandleEvadeBuggedMob(args, e); } else if (e.Args[14].ToString() == "IMMUNE") { WoWUnit unit = e.DestUnit; if (unit != null && !unit.IsPlayer) { Logger.WriteDebug("{0} is immune to {1} spell school", unit.Name, e.SpellSchool); SpellImmunityManager.Add(unit.Entry, e.SpellSchool, e.Spell); } if (StyxWoW.Me.Class == WoWClass.Rogue && e.SpellId == 6770) { WoWUnit unitImmune = unit; if (unitImmune == null) { unitImmune = ObjectManager.GetObjectByGuid <WoWUnit>(Singular.ClassSpecific.Rogue.Common.lastSapTarget); } Singular.ClassSpecific.Rogue.Common.AddEntryToSapImmuneList(unitImmune); } } break; case "UNIT_DIED": if (StyxWoW.Me.CurrentTarget != null && e.DestGuid == StyxWoW.Me.CurrentTarget.Guid) { Spell.LastSpellCast = ""; } try { WoWUnit corpse = e.SourceUnit; WoWPartyMember pm = Unit.GroupMemberInfos.First(m => m.Guid == corpse.Guid); Logger.WriteDiagnostic("Combat Log: UNIT_DIED - Role={0} {1}", pm.Role & (~WoWPartyMember.GroupRole.Leader), corpse.SafeName()); } catch { } break; } }
public override void Initialize() { DateTime timeStart = DateTime.UtcNow; Logger.WriteFile("Initialize: started"); // cannot call method which references SingularSettings TalentManager.Init(); // initializes CurrentSpec which is referenced everywhere SingularSettings.Initialize(); // loads Singular global and spec-specific settings (must determine spec first) DetermineCurrentWoWContext(); Task.Run(() => WriteSupportInfo()); _lastLogLevel = GlobalSettings.Instance.LogLevel; // When we actually need to use it, we will. Spell.Init(); Spell.GcdInitialize(); EventHandlers.Init(); MountManager.Init(); HotkeyDirector.Init(); MovementManager.Init(); // SoulstoneManager.Init(); // switch to using Death behavior Dispelling.Init(); PartyBuff.Init(); Singular.Lists.BossList.Init(); Targeting.Instance.WeighTargetsFilter += PullMoreWeighTargetsFilter; //Logger.Write("Combat log event handler started."); // Do this now, so we ensure we update our context when needed. BotEvents.Player.OnMapChanged += e => { // Don't run this handler if we're not the current routine! if (!WeAreTheCurrentCombatRoutine) { return; } // Only ever update the context. All our internal handlers will use the context changed event // so we're not reliant on anything outside of ourselves for updates. UpdateContext(); }; TreeHooks.Instance.HooksCleared += () => { // Don't run this handler if we're not the current routine! if (!WeAreTheCurrentCombatRoutine) { return; } Logger.Write(LogColor.Hilite, "Hooks cleared, re-creating behaviors"); RebuildBehaviors(silent: true); Spell.GcdInitialize(); // probably not needed, but quick }; GlobalSettings.Instance.PropertyChanged += (sender, e) => { // Don't run this handler if we're not the current routine! if (!WeAreTheCurrentCombatRoutine) { return; } // only LogLevel change will impact our behav trees // .. as we conditionally include/omit some diagnostic nodes if debugging // also need to keep a cached copy of prior value as the event // .. fires on the settor, not when the value is different if (e.PropertyName == "LogLevel" && _lastLogLevel != GlobalSettings.Instance.LogLevel) { _lastLogLevel = GlobalSettings.Instance.LogLevel; Logger.Write(LogColor.Hilite, "HonorBuddy {0} setting changed to {1}, re-creating behaviors", e.PropertyName, _lastLogLevel.ToString()); RebuildBehaviors(); Spell.GcdInitialize(); // probably not needed, but quick } }; // install botevent handler so we can consolidate validation on whether // .. local botevent handlers should be called or not SingularBotEventInitialize(); Logger.Write("Determining talent spec."); try { TalentManager.Update(); } catch (Exception e) { StopBot(e.ToString()); } Logger.Write("Current spec is " + SpecName()); // write current settings to log file... only written at startup and when Save press in Settings UI Task.Run(() => SingularSettings.Instance.LogSettings()); // Update the current WoWContext, and fire an event for the change. UpdateContext(); // NOTE: Hook these events AFTER the context update. OnWoWContextChanged += (orig, ne) => { Logger.Write(LogColor.Hilite, "Context changed, re-creating behaviors"); SingularRoutine.DescribeContext(); RebuildBehaviors(); Spell.GcdInitialize(); Singular.Lists.BossList.Init(); }; RoutineManager.Reloaded += (s, e) => { Logger.Write(LogColor.Hilite, "Routines were reloaded, re-creating behaviors"); RebuildBehaviors(silent: true); Spell.GcdInitialize(); }; // create silently since Start button will create a context change (at least first Start) // .. which will build behaviors again if (!Instance.RebuildBehaviors()) { return; } // if (IsPluginEnabled("DrinkPotions")) { Logger.Write(LogColor.Hilite, "info: disabling DrinkPotions plugin, conflicts with Singular potion support"); SetPluginEnabled("DrinkPotions", false); } SpellImmunityManager.Add(16292, WoWSpellSchool.Frost); // http://www.wowhead.com/npc=16292/aquantion Logger.WriteDebug(Color.White, "Verified behaviors can be created!"); Logger.Write("Initialization complete!"); Logger.WriteDiagnostic(Color.White, "Initialize: completed taking {0:F2} seconds", (DateTime.UtcNow - timeStart).TotalSeconds); }
// Thanks to Singular Devs for the CombatLogEventArgs class and SpellImmunityManager. private static void HandleCombatLog(object sender, LuaEventArgs args) { var e = new CombatLogEventArgs(args.EventName, args.FireTimeStamp, args.Args); //var missType = Convert.ToString(e.Args[14]); switch (e.Event) { case "SWING_MISSED": if (e.Args[11].ToString() == "EVADE") { CLULogger.TroubleshootLog("Mob is evading swing. Blacklisting it!"); Blacklist.Add(e.DestGuid, TimeSpan.FromMinutes(30)); if (StyxWoW.Me.CurrentTargetGuid == e.DestGuid) { StyxWoW.Me.ClearTarget(); } BotPoi.Clear("Blacklisting evading mob"); StyxWoW.SleepForLagDuration(); } else if (e.Args[11].ToString() == "IMMUNE") { WoWUnit unit = e.DestUnit; if (unit != null && !unit.IsPlayer) { CLULogger.TroubleshootLog("{0} is immune to {1} spell school", unit.Name, e.SpellSchool); SpellImmunityManager.Add(unit.Entry, e.SpellSchool); } } break; case "SPELL_MISSED": case "RANGE_MISSED": if (e.Args[14].ToString() == "EVADE") { CLULogger.TroubleshootLog("Mob is evading ranged attack. Blacklisting it!"); Blacklist.Add(e.DestGuid, TimeSpan.FromMinutes(30)); if (StyxWoW.Me.CurrentTargetGuid == e.DestGuid) { StyxWoW.Me.ClearTarget(); } BotPoi.Clear("Blacklisting evading mob"); StyxWoW.SleepForLagDuration(); } else if (e.Args[14].ToString() == "IMMUNE") { WoWUnit unit = e.DestUnit; if (unit != null && !unit.IsPlayer) { CLULogger.TroubleshootLog("{0} is immune to {1} spell school", unit.Name, e.SpellSchool); SpellImmunityManager.Add(unit.Entry, e.SpellSchool); } } break; case "SPELL_AURA_REFRESH": if (e.SourceGuid == StyxWoW.Me.Guid) { if (e.SpellId == 1822) { Classes.Druid.Common.RakeMultiplier = 1; //TF if (StyxWoW.Me.HasAura(5217)) { Classes.Druid.Common.RakeMultiplier = Classes.Druid.Common.RakeMultiplier * 1.15; } //Savage Roar if (StyxWoW.Me.HasAura(127538)) { Classes.Druid.Common.RakeMultiplier = Classes.Druid.Common.RakeMultiplier * 1.3; } //Doc if (StyxWoW.Me.HasAura(108373)) { Classes.Druid.Common.RakeMultiplier = Classes.Druid.Common.RakeMultiplier * 1.25; } } if (e.SpellId == 1079) { Classes.Druid.Common.ExtendedRip = 0; Classes.Druid.Common.RipMultiplier = 1; //TF if (StyxWoW.Me.HasAura(5217)) { Classes.Druid.Common.RipMultiplier = Classes.Druid.Common.RipMultiplier * 1.15; } //Savage Roar if (StyxWoW.Me.HasAura(127538)) { Classes.Druid.Common.RipMultiplier = Classes.Druid.Common.RipMultiplier * 1.3; } //Doc if (StyxWoW.Me.HasAura(108373)) { Classes.Druid.Common.RipMultiplier = Classes.Druid.Common.RipMultiplier * 1.25; } } } break; case "SPELL_AURA_APPLIED": if (e.SourceGuid == StyxWoW.Me.Guid) { if (e.SpellId == 1822) { Classes.Druid.Common.RakeMultiplier = 1; //TF if (StyxWoW.Me.HasAura(5217)) { Classes.Druid.Common.RakeMultiplier = Classes.Druid.Common.RakeMultiplier * 1.15; } //Savage Roar if (StyxWoW.Me.HasAura(127538)) { Classes.Druid.Common.RakeMultiplier = Classes.Druid.Common.RakeMultiplier * 1.3; } //Doc if (StyxWoW.Me.HasAura(108373)) { Classes.Druid.Common.RakeMultiplier = Classes.Druid.Common.RakeMultiplier * 1.25; } } if (e.SpellId == 1079) { Classes.Druid.Common.ExtendedRip = 0; Classes.Druid.Common.RipMultiplier = 1; //TF if (StyxWoW.Me.HasAura(5217)) { Classes.Druid.Common.RipMultiplier = Classes.Druid.Common.RipMultiplier * 1.15; } //Savage Roar if (StyxWoW.Me.HasAura(127538)) { Classes.Druid.Common.RipMultiplier = Classes.Druid.Common.RipMultiplier * 1.3; } //Doc if (StyxWoW.Me.HasAura(108373)) { Classes.Druid.Common.RipMultiplier = Classes.Druid.Common.RipMultiplier * 1.25; } } } break; case "SPELL_AURA_REMOVED": if (e.SourceGuid == StyxWoW.Me.Guid) { if (e.SpellId == 1822) { Classes.Druid.Common.RakeMultiplier = 0; } if (e.SpellId == 1079) { Classes.Druid.Common.ExtendedRip = 0; Classes.Druid.Common.RipMultiplier = 0; } } break; } }
private static void HandleCombatLog(object sender, LuaEventArgs args) { var e = new CombatLogEventArgs(args.EventName, args.FireTimeStamp, args.Args); if (e.SourceGuid != StyxWoW.Me.Guid) { return; } // Logger.WriteDebug("[CombatLog] " + e.Event + " - " + e.SourceName + " - " + e.SpellName); switch (e.Event) { default: Logger.WriteDebug("[CombatLog] filter out this event -- " + e.Event + " - " + e.SourceName + " - " + e.SpellName); break; case "SPELL_CAST_FAILED": if (SingularSettings.Instance.EnableDebugLogging) { Logger.WriteDebug("[CombatLog] {0}:{1} cast of {2}#{3} failed: '{4}'", e.SourceName, e.SourceGuid, e.SpellName, e.SpellId, e.Args[14] ); } break; case "SPELL_AURA_APPLIED": case "SPELL_CAST_SUCCESS": if (e.SourceGuid != StyxWoW.Me.Guid) { return; } // Update the last spell we cast. So certain classes can 'switch' their logic around. Spell.LastSpellCast = e.SpellName; //Logger.WriteDebug("Successfully cast " + Spell.LastSpellCast); // Force a wait for all summoned minions. This prevents double-casting it. if (StyxWoW.Me.Class == WoWClass.Warlock && e.SpellName.StartsWith("Summon ")) { StyxWoW.SleepForLagDuration(); } break; case "SWING_MISSED": if (e.Args[11].ToString() == "EVADE") { Logger.Write("Mob is evading swing. Blacklisting it!"); Blacklist.Add(e.DestGuid, TimeSpan.FromMinutes(30)); if (StyxWoW.Me.CurrentTargetGuid == e.DestGuid) { StyxWoW.Me.ClearTarget(); } BotPoi.Clear("Blacklisting evading mob"); StyxWoW.SleepForLagDuration(); } else if (e.Args[11].ToString() == "IMMUNE") { WoWUnit unit = e.DestUnit; if (unit != null && !unit.IsPlayer) { Logger.WriteDebug("{0} is immune to {1} spell school", unit.Name, e.SpellSchool); SpellImmunityManager.Add(unit.Entry, e.SpellSchool); } } break; case "SPELL_MISSED": case "RANGE_MISSED": // DoT casting spam can occur when running on test dummy with low +hit // .. and multiple misses occurring. this should help troubleshoot // .. false reports of flawed rotation if (SingularSettings.Instance.EnableDebugLogging) { Logger.WriteFile( "[CombatLog] {0} {1}#{2} {3}", e.Event, e.SpellName, e.SpellId, e.Args[14] ); } if (e.Args[14].ToString() == "EVADE") { Logger.Write("Mob is evading ranged attack. Blacklisting it!"); Blacklist.Add(e.DestGuid, TimeSpan.FromMinutes(30)); if (StyxWoW.Me.CurrentTargetGuid == e.DestGuid) { StyxWoW.Me.ClearTarget(); } BotPoi.Clear("Blacklisting evading mob"); StyxWoW.SleepForLagDuration(); } else if (e.Args[14].ToString() == "IMMUNE") { WoWUnit unit = e.DestUnit; if (unit != null && !unit.IsPlayer) { Logger.WriteDebug("{0} is immune to {1} spell school", unit.Name, e.SpellSchool); SpellImmunityManager.Add(unit.Entry, e.SpellSchool); } } break; } }
private static void HandleCombatLog(object sender, LuaEventArgs args) { if (RoutineManager.Current.Name != AdvancedAI.Instance.Name) { return; } var e = new CombatLogEventArgs(args.EventName, args.FireTimeStamp, args.Args); if (e.SourceGuid != StyxWoW.Me.Guid) { return; } // Logger.WriteDebug("[CombatLog] " + e.Event + " - " + e.SourceName + " - " + e.SpellName); switch (e.Event) { // spell_cast_failed only passes filter in Singular debug mode case "SPELL_CAST_FAILED": Logging.WriteDiagnostic("[CombatLog] {0} {1}#{2} failure: '{3}'", e.Event, e.Spell.Name, e.SpellId, e.Args[14]); if (e.Args[14].ToString() == LocalizedLineOfSightFailure) { ulong guid; try { LastLineOfSightTarget = e.DestUnit; guid = LastLineOfSightTarget == null ? 0 : LastLineOfSightTarget.Guid; } catch { LastLineOfSightTarget = StyxWoW.Me.CurrentTarget; guid = StyxWoW.Me.CurrentTargetGuid; } LastLineOfSightFailure = DateTime.Now; Logging.WriteDiagnostic("[CombatLog] cast fail due to los reported at {0} on target {1:X}", LastLineOfSightFailure.ToString("HH:mm:ss.fff"), e.DestGuid); } else if (StyxWoW.Me.Class == WoWClass.Druid) { if (LocalizedShapeshiftMessages.ContainsKey(e.Args[14].ToString())) { string symbolicName = LocalizedShapeshiftMessages[e.Args[14].ToString()]; LastShapeshiftFailure = DateTime.Now; Logging.WriteDiagnostic("[CombatLog] cast fail due to shapeshift error '{0}' while questing reported at {1}", symbolicName, LastShapeshiftFailure.ToString("HH:mm:ss.fff")); } } else if (StyxWoW.Me.Class == WoWClass.Rogue) { if (e.Args[14].ToString() == LocalizedNoPocketsToPickFailure) { // args on this event don't match standard SPELL_CAST_FAIL // -- so, Singular only casts on current target so use that assumption WoWUnit unit = StyxWoW.Me.CurrentTarget; if (unit == null) { Logging.WriteDiagnostic("[CombatLog] has no pockets event did not have a valid unit"); } else { Logging.WriteDiagnostic("[CombatLog] {0} has no pockets, blacklisting from pick pocket for 2 minutes", unit.SafeName()); Blacklist.Add(unit.Guid, BlacklistFlags.Node, TimeSpan.FromMinutes(2)); } } } break; #if SOMEONE_USES_LAST_SPELL_AT_SOME_POINT case "SPELL_AURA_APPLIED": case "SPELL_CAST_SUCCESS": if (e.SourceGuid != StyxWoW.Me.Guid) { return; } // Update the last spell we cast. So certain classes can 'switch' their logic around. Spell.LastSpellCast = e.SpellName; //Logger.WriteDebug("Successfully cast " + Spell.LastSpellCast); // following commented block should not be needed since rewrite of Pet summon // //// Force a wait for all summoned minions. This prevents double-casting it. //if (StyxWoW.Me.Class == WoWClass.Warlock && e.SpellName.StartsWith("Summon ")) //{ // StyxWoW.SleepForLagDuration(); //} break; #endif case "SWING_MISSED": if (e.Args[11].ToString() == "EVADE") { HandleEvadeBuggedMob(args, e); } else if (e.Args[11].ToString() == "IMMUNE") { WoWUnit unit = e.DestUnit; if (unit != null && !unit.IsPlayer) { Logging.WriteDiagnostic("{0} is immune to Physical spell school", unit.Name); SpellImmunityManager.Add(unit.Entry, WoWSpellSchool.Physical); } } break; case "SPELL_MISSED": case "RANGE_MISSED": if (e.Args[14].ToString() == "EVADE") { HandleEvadeBuggedMob(args, e); } else if (e.Args[14].ToString() == "IMMUNE") { WoWUnit unit = e.DestUnit; if (unit != null && !unit.IsPlayer) { Logging.WriteDiagnostic("{0} is immune to {1} spell school", unit.Name, e.SpellSchool); SpellImmunityManager.Add(unit.Entry, e.SpellSchool); } } break; } }