private void HandleDamageProcessed(object sender, DamageProcessedEvent processed) { if (LastFightProcessTime != processed.BeginTime) { DataManager.Instance.CheckExpireFights(processed.BeginTime); ValidCombo.Clear(); if (processed.BeginTime - LastFightProcessTime > RECENTSPELLTIME) { RecentSpellCache.Clear(); } } // cache recent player spells to help determine who the caster was var isAttackerPlayer = PlayerManager.Instance.IsPetOrPlayer(processed.Record.Attacker) || processed.Record.Attacker == Labels.RS; if (isAttackerPlayer && (processed.Record.Type == Labels.DD || processed.Record.Type == Labels.DOT || processed.Record.Type == Labels.PROC)) { RecentSpellCache[processed.Record.SubType] = true; } string comboKey = processed.Record.Attacker + "=" + processed.Record.Defender; if (ValidCombo.TryGetValue(comboKey, out bool defender) || IsValidAttack(processed.Record, isAttackerPlayer, out defender)) { ValidCombo[comboKey] = defender; bool isNonTankingFight = false; string origTimeString = processed.OrigTimeString.Substring(4, 15); // fix for unknown spells having a good name to work from if (processed.Record.AttackerIsSpell && defender) { processed.Record.Attacker = Labels.UNK; } Fight fight = Get(processed.Record, processed.BeginTime, origTimeString, defender); if (defender) { Helpers.AddAction(fight.DamageBlocks, processed.Record, processed.BeginTime); AddPlayerTime(fight, processed.Record, processed.Record.Attacker, processed.BeginTime); fight.BeginDamageTime = double.IsNaN(fight.BeginDamageTime) ? processed.BeginTime : fight.BeginDamageTime; fight.LastDamageTime = processed.BeginTime; if (StatsUtil.IsHitType(processed.Record.Type)) { fight.DamageHits++; fight.Total += processed.Record.Total; isNonTankingFight = fight.DamageHits == 1; var attacker = processed.Record.AttackerOwner ?? processed.Record.Attacker; if (fight.PlayerTotals.TryGetValue(attacker, out FightTotalDamage total)) { total.Damage += (processed.Record.Type == Labels.BANE) ? 0 : processed.Record.Total; total.DamageWithBane += processed.Record.Total; total.Name = processed.Record.Attacker; total.PetOwner = total.PetOwner ?? processed.Record.AttackerOwner; total.UpdateTime = processed.BeginTime; } else { fight.PlayerTotals[attacker] = new FightTotalDamage { Damage = (processed.Record.Type == Labels.BANE) ? 0 : processed.Record.Total, DamageWithBane = processed.Record.Total, Name = processed.Record.Attacker, PetOwner = processed.Record.AttackerOwner, UpdateTime = processed.BeginTime, BeginTime = processed.BeginTime }; } SpellDamageStats stats = null; var spellKey = processed.Record.Attacker + "++" + processed.Record.SubType; if (processed.Record.Type == Labels.DD) { if (!fight.DDDamage.TryGetValue(spellKey, out stats)) { stats = new SpellDamageStats { Caster = processed.Record.Attacker, Spell = processed.Record.SubType }; fight.DDDamage[spellKey] = stats; } } else if (processed.Record.Type == Labels.DOT) { if (!fight.DoTDamage.TryGetValue(spellKey, out stats)) { stats = new SpellDamageStats { Caster = processed.Record.Attacker, Spell = processed.Record.SubType }; fight.DoTDamage[spellKey] = stats; } } if (stats != null) { stats.Count += 1; stats.Max = Math.Max(processed.Record.Total, stats.Max); stats.Total += processed.Record.Total; } // only a pet can 'hit' with a Flurry since players only crush/slash/punch/pierce with main hand weapons if (processed.Record.AttackerOwner == null && processed.Record.Type == Labels.MELEE && processed.Record.SubType == "Hits" && LineModifiersParser.IsFlurry(processed.Record.ModifiersMask)) { PlayerManager.Instance.AddVerifiedPet(processed.Record.Attacker); } } } else { Helpers.AddAction(fight.TankingBlocks, processed.Record, processed.BeginTime); AddPlayerTime(fight, processed.Record, processed.Record.Defender, processed.BeginTime); fight.BeginTankingTime = double.IsNaN(fight.BeginTankingTime) ? processed.BeginTime : fight.BeginTankingTime; fight.LastTankingTime = processed.BeginTime; if (StatsUtil.IsHitType(processed.Record.Type)) { fight.TankHits++; } } fight.LastTime = processed.BeginTime; LastFightProcessTime = processed.BeginTime; var ttl = fight.LastTime - fight.BeginTime + 1; fight.TooltipText = string.Format(CultureInfo.CurrentCulture, "#Hits To Players: {0}, #Hits From Players: {1}, Time Alive: {2}s", fight.TankHits, fight.DamageHits, ttl); DataManager.Instance.UpdateIfNewFightMap(fight.CorrectMapKey, fight, isNonTankingFight); } }
internal static CombinedStats ComputeOverlayStats(int mode, int maxRows, string selectedClass) { CombinedStats combined = null; var allDamage = 0L; var allTime = new TimeRange(); var playerTotals = new Dictionary <string, OverlayPlayerTotal>(); var playerHasPet = new Dictionary <string, bool>(); var updateTime = 0d; var oldestTime = 0d; var fights = DataManager.Instance.GetOverlayFights(); Fight oldestFight = null; bool baneEnabled = MainWindow.IsBaneDamageEnabled; // clear out anything pending in the queue DamageLineParser.CheckSlainQueue(DateUtil.ToDouble(DateTime.Now.AddSeconds(-3))); if (fights.Count > 0) { oldestFight = fights[0]; foreach (var fight in fights.Where(fight => !fight.Dead || mode > 0)) { foreach (var keypair in fight.PlayerTotals) { var player = keypair.Key; if (!string.IsNullOrEmpty(keypair.Value.PetOwner)) { player = keypair.Value.PetOwner; playerHasPet[player] = true; } else if (PlayerManager.Instance.GetPlayerFromPet(player) is string owner && owner != Labels.UNASSIGNED) { player = owner; playerHasPet[player] = true; } allDamage += baneEnabled ? keypair.Value.DamageWithBane : keypair.Value.Damage; allTime.Add(new TimeSegment(keypair.Value.BeginTime, fight.LastDamageTime)); if (updateTime == 0) { updateTime = keypair.Value.UpdateTime; oldestTime = keypair.Value.UpdateTime; oldestFight = fight; } else { updateTime = Math.Max(updateTime, keypair.Value.UpdateTime); if (oldestTime > keypair.Value.UpdateTime) { oldestTime = keypair.Value.UpdateTime; oldestFight = fight; } } if (playerTotals.TryGetValue(player, out OverlayPlayerTotal total)) { total.Damage += baneEnabled ? keypair.Value.DamageWithBane : keypair.Value.Damage; total.Range.Add(new TimeSegment(keypair.Value.BeginTime, keypair.Value.UpdateTime)); total.UpdateTime = Math.Max(total.UpdateTime, keypair.Value.UpdateTime); } else { playerTotals[player] = new OverlayPlayerTotal { Name = player, Damage = baneEnabled ? keypair.Value.DamageWithBane : keypair.Value.Damage, Range = new TimeRange(new TimeSegment(keypair.Value.BeginTime, keypair.Value.UpdateTime)), UpdateTime = keypair.Value.UpdateTime }; } } } var timeout = mode == 0 ? DataManager.FIGHTTIMEOUT : mode; var totalSeconds = allTime.GetTotal(); if (oldestFight != null && totalSeconds > 0 && allDamage > 0 && (DateTime.Now - DateTime.MinValue.AddSeconds(updateTime)).TotalSeconds <= timeout) { int rank = 1; var list = new List <PlayerStats>(); var totalDps = (long)Math.Round(allDamage / totalSeconds, 2); int myIndex = -1; foreach (var total in playerTotals.Values.OrderByDescending(total => total.Damage)) { var time = total.Range.GetTotal(); if (time > 0 && (DateTime.Now - DateTime.MinValue.AddSeconds(total.UpdateTime)).TotalSeconds <= DataManager.MAXTIMEOUT) { PlayerStats playerStats = new PlayerStats() { Name = playerHasPet.ContainsKey(total.Name) ? total.Name + " +Pets" : total.Name, Total = total.Damage, DPS = (long)Math.Round(total.Damage / time, 2), TotalSeconds = time, Rank = (ushort)rank++, ClassName = PlayerManager.Instance.GetPlayerClass(total.Name), OrigName = total.Name }; if (playerStats.Name.StartsWith(ConfigUtil.PlayerName, StringComparison.Ordinal)) { myIndex = list.Count; } if (myIndex == list.Count || selectedClass == Properties.Resources.ANY_CLASS || selectedClass == playerStats.ClassName) { list.Add(playerStats); } } } if (myIndex > (maxRows - 1)) { var me = list[myIndex]; list = list.Take(maxRows - 1).ToList(); list.Add(me); } else { list = list.Take(maxRows).ToList(); } combined = new CombinedStats(); combined.StatsList.AddRange(list); combined.RaidStats = new PlayerStats { Total = allDamage, DPS = totalDps, TotalSeconds = totalSeconds }; combined.TargetTitle = (fights.Count > 1 ? "C(" + fights.Count + "): " : "") + oldestFight.Name; // these are here to support copy/paste of the parse combined.TimeTitle = string.Format(CultureInfo.CurrentCulture, StatsUtil.TIME_FORMAT, combined.RaidStats.TotalSeconds); combined.TotalTitle = string.Format(CultureInfo.CurrentCulture, StatsUtil.TOTAL_FORMAT, StatsUtil.FormatTotals(combined.RaidStats.Total), " Damage ", StatsUtil.FormatTotals(combined.RaidStats.DPS)); } } return(combined); }