private static void LogUndesirableEvent(string p, CombatLogEventArgs e) { if (SingularSettings.Debug) { string sourceName; string destName; try { sourceName = e.SourceUnit.SafeName(); } catch { sourceName = "unknown"; } try { destName = e.DestUnit.SafeName(); } catch { destName = "unknown"; } Logger.WriteDiagnostic("Programmer Error - Combat Log Event {0}: filter out {1} - {2} {3} - {4} {5} on {6} {7}", p, e.EventName, e.SourceGuid, sourceName, e.SpellName, e.SpellId, e.DestGuid, destName ); } }
private static void HandleCombatLog(object sender, LuaEventArgs args) { var e = new CombatLogEventArgs(args.EventName, args.FireTimeStamp, args.Args); //Logger.WriteDebug("[CombatLog] " + e.Event + " - " + e.SourceName + " - " + e.SpellName); switch (e.Event) { 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 (SingularRoutine.MyClass == WoWClass.Warlock && e.SpellName.StartsWith("Summon ")) { StyxWoW.SleepForLagDuration(); } break; case "SPELL_MISSED": if (e.Args[14].ToString() == "EVADE") { Logger.Write("Mob is evading. 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(); } break; } }
private static void HandleEvadeBuggedMob(LuaEventArgs args, CombatLogEventArgs e) { WoWUnit unit = e.DestUnit; WoWGuid guid = e.DestGuid; if (unit == null && StyxWoW.Me.GotTarget()) { unit = StyxWoW.Me.CurrentTarget; guid = StyxWoW.Me.CurrentTargetGuid; Logger.Write("Evade: bugged mob guid:{0}, so assuming current target instead", args.Args[7]); } if (unit != null) { if (!MobsThatEvaded.ContainsKey(unit.Guid)) { MobsThatEvaded.Add(unit.Guid, 0); } MobsThatEvaded[unit.Guid] = MobsThatEvaded[unit.Guid] + 1; if (MobsThatEvaded[unit.Guid] < SingularSettings.Instance.EvadedAttacksAllowed) { Logger.Write("Mob {0} has evaded {1} times. Not blacklisting yet, but will count evades on {2:X0} for now", unit.SafeName(), MobsThatEvaded[unit.Guid], unit.Guid); } else { const int MinutesToBlacklist = 5; if (Blacklist.Contains(unit.Guid, BlacklistFlags.Combat)) { Logger.Write(Color.LightGoldenrodYellow, "Mob {0} has evaded {1} times. Previously blacklisted {2:X0} for {3} minutes!", unit.SafeName(), MobsThatEvaded[unit.Guid], unit.Guid, MinutesToBlacklist); } else { string fragment = string.Format("Mob {0} has evaded {1} times", unit.SafeName(), MobsThatEvaded[unit.Guid]); Logger.Write(Color.LightGoldenrodYellow, "{0}. Blacklisting {1:X0} for {2} minutes!", fragment, unit.Guid, MinutesToBlacklist); Blacklist.Add(unit.Guid, BlacklistFlags.Combat, TimeSpan.FromMinutes(MinutesToBlacklist), "Singular - " + fragment); if (!Blacklist.Contains(unit.Guid, BlacklistFlags.Combat)) { Logger.Write(Color.Pink, "error: blacklist does not contain entry for {0} after Blacklist.Add", unit.SafeName()); } } if (BotPoi.Current.Guid == unit.Guid) { Logger.Write("EvadeHandling: Current BotPOI type={0} is Evading, clearing now...", BotPoi.Current.Type); BotPoi.Clear("Singular recognized Evade bugged mob"); } if (StyxWoW.Me.CurrentTargetGuid == guid) { foreach (var target in Targeting.Instance.TargetList) { if (Unit.ValidUnit(target) && !Blacklist.Contains(target.Guid, BlacklistFlags.Pull | BlacklistFlags.Combat) && unit.EvadedAttacksCount() < SingularSettings.Instance.EvadedAttacksAllowed ) { Logger.Write(Color.Pink, "Setting target to {0} to get off evade bugged mob!", target.SafeName()); target.Target(); return; } } Logger.Write(Color.Pink, "BotBase has 0 entries in Target list not blacklisted -- nothing else we can do at this point!"); // StyxWoW.Me.ClearTarget(); } } } /// line below was originally in Evade logic, but commenting to avoid Sleeps // StyxWoW.SleepForLagDuration(); }
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; } }
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; } }