private void ComputeDamageStats(GenerateStatsOptions options) { lock (DamageGroupIds) { if (RaidTotals != null) { CombinedStats combined = null; ConcurrentDictionary <string, Dictionary <string, PlayerStats> > childrenStats = new ConcurrentDictionary <string, Dictionary <string, PlayerStats> >(); ConcurrentDictionary <string, PlayerStats> topLevelStats = new ConcurrentDictionary <string, PlayerStats>(); Dictionary <string, PlayerStats> individualStats = new Dictionary <string, PlayerStats>(); // always start over RaidTotals.Total = 0; double stopTime = -1; try { FireChartEvent(options, "UPDATE"); if (options.RequestSummaryData) { if (options.MaxSeconds > -1 && options.MaxSeconds <= RaidTotals.MaxTime && options.MaxSeconds != RaidTotals.TotalSeconds) { var filteredGroups = new List <List <ActionBlock> >(); AllDamageGroups.ForEach(group => { var filteredBlocks = new List <ActionBlock>(); filteredGroups.Add(filteredBlocks); group.ForEach(block => { stopTime = stopTime == -1 ? block.BeginTime + options.MaxSeconds : stopTime; if (block.BeginTime <= stopTime) { filteredBlocks.Add(block); } }); }); DamageGroups = filteredGroups; RaidTotals.TotalSeconds = options.MaxSeconds; } else { DamageGroups = AllDamageGroups; } DamageGroups.ForEach(group => { group.ForEach(block => { block.Actions.ForEach(action => { if (action is DamageRecord record) { var stats = StatsUtil.CreatePlayerStats(individualStats, record.Attacker); if (!MainWindow.IsBaneDamageEnabled && record.Type == Labels.BANE) { stats.BaneHits++; if (individualStats.TryGetValue(stats.OrigName + " +Pets", out PlayerStats temp)) { temp.BaneHits++; } } else { RaidTotals.Total += record.Total; bool isAttackerPet = PlayerManager.Instance.IsVerifiedPet(record.Attacker); StatsUtil.UpdateStats(stats, record, isAttackerPet); if ((!PetToPlayer.TryGetValue(record.Attacker, out string player) && !PlayerPets.ContainsKey(record.Attacker)) || player == Labels.UNASSIGNED) { topLevelStats[record.Attacker] = stats; stats.IsTopLevel = true; } else { string origName = player ?? record.Attacker; string aggregateName = origName + " +Pets"; PlayerStats aggregatePlayerStats = StatsUtil.CreatePlayerStats(individualStats, aggregateName, origName); StatsUtil.UpdateStats(aggregatePlayerStats, record, isAttackerPet); topLevelStats[aggregateName] = aggregatePlayerStats; if (!childrenStats.TryGetValue(aggregateName, out Dictionary <string, PlayerStats> children)) { childrenStats[aggregateName] = new Dictionary <string, PlayerStats>(); } childrenStats[aggregateName][stats.Name] = stats; stats.IsTopLevel = false; } PlayerSubStats subStats = StatsUtil.CreatePlayerSubStats(stats.SubStats, record.SubType, record.Type); uint critHits = subStats.CritHits; StatsUtil.UpdateStats(subStats, record, isAttackerPet); // dont count misses/dodges or where no damage was done if (record.Total > 0) { Dictionary <long, int> values = subStats.CritHits > critHits ? subStats.CritFreqValues : subStats.NonCritFreqValues; Helpers.LongIntAddHelper.Add(values, record.Total, 1); } } } }); }); });
private void ComputeHealingStats(GenerateStatsOptions options) { lock (HealingGroups) { if (RaidTotals != null) { CombinedStats combined = null; Dictionary <string, PlayerStats> individualStats = new Dictionary <string, PlayerStats>(); // always start over RaidTotals.Total = 0; try { FireChartEvent(options, "UPDATE"); if (options.RequestSummaryData) { HealingGroups.ForEach(group => { group.ForEach(block => { block.Actions.ForEach(action => { if (action is HealRecord record) { RaidTotals.Total += record.Total; PlayerStats stats = StatsUtil.CreatePlayerStats(individualStats, record.Healer); StatsUtil.UpdateStats(stats, record); var spellStatName = record.SubType ?? Labels.SELFHEAL; PlayerSubStats spellStats = StatsUtil.CreatePlayerSubStats(stats.SubStats, spellStatName, record.Type); StatsUtil.UpdateStats(spellStats, record); var healedStatName = record.Healed; PlayerSubStats healedStats = StatsUtil.CreatePlayerSubStats(stats.SubStats2, healedStatName, record.Type); StatsUtil.UpdateStats(healedStats, record); } }); }); }); RaidTotals.DPS = (long)Math.Round(RaidTotals.Total / RaidTotals.TotalSeconds, 2); Parallel.ForEach(individualStats.Values, stats => UpdateStats(stats, HealerSpellTimeRanges, HealerHealedTimeRanges)); combined = new CombinedStats { RaidStats = RaidTotals, TargetTitle = (Selected.Count > 1 ? "Combined (" + Selected.Count + "): " : "") + Title, TimeTitle = string.Format(CultureInfo.CurrentCulture, StatsUtil.TIME_FORMAT, RaidTotals.TotalSeconds), TotalTitle = string.Format(CultureInfo.CurrentCulture, StatsUtil.TOTAL_FORMAT, StatsUtil.FormatTotals(RaidTotals.Total), " Heals ", StatsUtil.FormatTotals(RaidTotals.DPS)) }; combined.StatsList.AddRange(individualStats.Values.AsParallel().OrderByDescending(item => item.Total)); combined.FullTitle = StatsUtil.FormatTitle(combined.TargetTitle, combined.TimeTitle, combined.TotalTitle); combined.ShortTitle = StatsUtil.FormatTitle(combined.TargetTitle, combined.TimeTitle, ""); for (int i = 0; i < combined.StatsList.Count; i++) { combined.StatsList[i].Rank = Convert.ToUInt16(i + 1); combined.UniqueClasses[combined.StatsList[i].ClassName] = 1; } } } #pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) #pragma warning restore CA1031 // Do not catch general exception types { if (ex is ArgumentNullException || ex is NullReferenceException || ex is ArgumentOutOfRangeException || ex is ArgumentException || ex is OutOfMemoryException) { LOG.Error(ex); } } if (options.RequestSummaryData) { // generating new stats var genEvent = new StatsGenerationEvent() { Type = Labels.HEALPARSE, State = "COMPLETED", CombinedStats = combined }; genEvent.Groups.AddRange(HealingGroups); EventsGenerationStatus?.Invoke(this, genEvent); } } } }
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); }
internal static Dictionary <string, List <HitFreqChartData> > GetHitFreqValues(PlayerStats selected, CombinedStats damageStats) { Dictionary <string, List <HitFreqChartData> > results = new Dictionary <string, List <HitFreqChartData> >(); // get chart data for player and pets if available if (damageStats?.Children.ContainsKey(selected.Name) == true) { damageStats?.Children[selected.Name].ForEach(stats => AddStats(stats)); } else { AddStats(selected); } return(results); void AddStats(PlayerStats stats) { results[stats.Name] = new List <HitFreqChartData>(); foreach (string type in stats.SubStats.Keys) { HitFreqChartData chartData = new HitFreqChartData() { HitType = stats.SubStats[type].Name }; // add crits chartData.CritXValues.AddRange(stats.SubStats[type].CritFreqValues.Keys.OrderBy(key => key)); chartData.CritXValues.ForEach(damage => chartData.CritYValues.Add(stats.SubStats[type].CritFreqValues[damage])); // add non crits chartData.NonCritXValues.AddRange(stats.SubStats[type].NonCritFreqValues.Keys.OrderBy(key => key)); chartData.NonCritXValues.ForEach(damage => chartData.NonCritYValues.Add(stats.SubStats[type].NonCritFreqValues[damage])); results[stats.Name].Add(chartData); } } }
internal TankingBreakdown(CombinedStats currentStats) { InitializeComponent(); titleLabel.Content = currentStats?.ShortTitle; RaidStats = currentStats?.RaidStats; }
private List <PlayerSubStats> GetSubStats(PlayerStats playerStats) { var name = playerStats.Name; List <PlayerSubStats> list = new List <PlayerSubStats>(); if (OtherDamage.ContainsKey(name)) { AddToList(playerStats, list, OtherDamage[name]); } if (GroupedDD.ContainsKey(name)) { PlayerSubStats dds = GroupedDD[name]; if (dds.Total > 0) { if (CurrentGroupDDSetting) { list.Add(dds); } else { AddToList(playerStats, list, UnGroupedDD[name]); } } } if (GroupedDoT.ContainsKey(name)) { PlayerSubStats dots = GroupedDoT[name]; if (dots.Total > 0) { if (CurrentGroupDoTSetting) { list.Add(dots); } else { AddToList(playerStats, list, UnGroupedDoT[name]); } } } if (GroupedProcs.ContainsKey(name)) { PlayerSubStats procs = GroupedProcs[name]; if (procs.Total > 0) { if (CurrentGroupProcsSetting) { list.Add(procs); } else { AddToList(playerStats, list, UnGroupedProcs[name]); } } } if (GroupedResisted.ContainsKey(name)) { if (UnGroupedResisted[name].Count > 0) { if (CurrentGroupResistedSetting) { list.Add(GroupedResisted[name]); } else { AddToList(playerStats, list, UnGroupedResisted[name]); } } } return(list); }
private void BuildGroups(PlayerStats playerStats, List <PlayerSubStats> all) { List <PlayerSubStats> list = new List <PlayerSubStats>(); PlayerSubStats dots = new PlayerSubStats() { Name = Labels.DOT, Type = Labels.DOT }; PlayerSubStats dds = new PlayerSubStats() { Name = Labels.DD, Type = Labels.DD }; PlayerSubStats procs = new PlayerSubStats() { Name = Labels.PROC, Type = Labels.PROC }; PlayerSubStats resisted = new PlayerSubStats() { Name = Labels.RESIST, Type = Labels.RESIST, ResistRate = 100 }; List <PlayerSubStats> allDots = new List <PlayerSubStats>(); List <PlayerSubStats> allDds = new List <PlayerSubStats>(); List <PlayerSubStats> allProcs = new List <PlayerSubStats>(); List <PlayerSubStats> allResisted = new List <PlayerSubStats>(); all.ForEach(sub => { PlayerSubStats stats = null; switch (sub.Type) { case Labels.DOT: stats = dots; allDots.Add(sub); break; case Labels.DD: case Labels.BANE: stats = dds; allDds.Add(sub); break; case Labels.PROC: stats = procs; allProcs.Add(sub); break; case Labels.RESIST: stats = resisted; allResisted.Add(sub); break; default: list.Add(sub); break; } StatsUtil.MergeStats(stats, sub); }); foreach (var stats in new PlayerSubStats[] { dots, dds, procs, resisted }) { StatsUtil.CalculateRates(stats, RaidStats, playerStats); } UnGroupedDD[playerStats.Name] = allDds; UnGroupedDoT[playerStats.Name] = allDots; UnGroupedProcs[playerStats.Name] = allProcs; UnGroupedResisted[playerStats.Name] = allResisted; GroupedDD[playerStats.Name] = dds; GroupedDoT[playerStats.Name] = dots; GroupedProcs[playerStats.Name] = procs; GroupedResisted[playerStats.Name] = resisted; OtherDamage[playerStats.Name] = list; Dispatcher.InvokeAsync(() => { if (allDds.Count > 0 && !groupDirectDamage.IsEnabled) { groupDirectDamage.IsEnabled = true; } if (allProcs.Count > 0 && !groupProcs.IsEnabled) { groupProcs.IsEnabled = true; } if (allDots.Count > 0 && !groupDoT.IsEnabled) { groupDoT.IsEnabled = true; } if (allResisted.Count > 0 && !groupResisted.IsEnabled) { groupResisted.IsEnabled = true; } }); }