internal object GetSortValue(PlayerSubStats sub) => sub?.GetType().GetProperty(CurrentSortKey).GetValue(sub, null);
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); } } } }
private static void UpdateSubStats(PlayerSubStats subStats, DamageRecord record, double beginTime) { StatsUtil.UpdateStats(subStats, record, beginTime); }
private void ComputeTankingStats(GenerateStatsOptions options) { lock (TankingGroups) { CombinedStats combined = null; Dictionary <string, PlayerStats> individualStats = new Dictionary <string, PlayerStats>(); if (RaidTotals != null) { // always start over RaidTotals.Total = 0; try { FireUpdateEvent(options); if (options.RequestSummaryData) { TankingGroups.ForEach(group => { // keep track of time range as well as the players that have been updated Dictionary <string, PlayerSubStats> allStats = new Dictionary <string, PlayerSubStats>(); group.ForEach(block => { block.Actions.ForEach(action => { if (action is DamageRecord record) { RaidTotals.Total += record.Total; PlayerStats stats = StatsUtil.CreatePlayerStats(individualStats, record.Defender); StatsUtil.UpdateStats(stats, record, block.BeginTime); allStats[record.Defender] = stats; PlayerSubStats subStats = StatsUtil.CreatePlayerSubStats(stats.SubStats, record.SubType, record.Type); UpdateSubStats(subStats, record, block.BeginTime); allStats[stats.Name + "=" + record.SubType] = subStats; } }); }); foreach (var stats in allStats.Values) { stats.TotalSeconds += stats.LastTime - stats.BeginTime + 1; stats.BeginTime = double.NaN; } }); RaidTotals.DPS = (long)Math.Round(RaidTotals.Total / RaidTotals.TotalSeconds, 2); Parallel.ForEach(individualStats.Values, stats => StatsUtil.UpdateCalculations(stats, RaidTotals)); 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), " Tanked ", 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; } } } catch (ArgumentNullException ane) { LOG.Error(ane); } catch (NullReferenceException nre) { LOG.Error(nre); } catch (ArgumentOutOfRangeException aro) { LOG.Error(aro); } FireCompletedEvent(options, combined, TankingGroups); } } }
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 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; } }); }