internal void PopulateHealing(CombinedStats combined) { lock (HealingGroups) { List <PlayerStats> playerStats = combined.StatsList; Dictionary <string, PlayerStats> individualStats = new Dictionary <string, PlayerStats>(); Dictionary <string, long> totals = new Dictionary <string, long>(); HealingGroups.ForEach(group => { group.ForEach(block => { block.Actions.ForEach(action => { if (action is HealRecord record) { PlayerStats stats = StatsUtil.CreatePlayerStats(individualStats, record.Healed); StatsUtil.UpdateStats(stats, record); PlayerSubStats subStats = StatsUtil.CreatePlayerSubStats(stats.SubStats, record.Healer, record.Type); StatsUtil.UpdateStats(subStats, record); var spellStatName = record.SubType ?? Labels.SELFHEAL; PlayerSubStats spellStats = StatsUtil.CreatePlayerSubStats(stats.SubStats2, spellStatName, record.Type); StatsUtil.UpdateStats(spellStats, record); long value = 0; if (totals.ContainsKey(record.Healed)) { value = totals[record.Healed]; } totals[record.Healed] = record.Total + value; } }); }); }); Parallel.ForEach(playerStats, stat => { if (individualStats.ContainsKey(stat.Name)) { if (totals.ContainsKey(stat.Name)) { stat.Extra = totals[stat.Name]; } var indStats = individualStats[stat.Name]; stat.SubStats2["receivedHealing"] = indStats; UpdateStats(indStats, HealedBySpellTimeRanges, HealedByHealerTimeRanges); indStats.SubStats.Values.ToList().ForEach(subStat => StatsUtil.UpdateCalculations(subStat, indStats)); indStats.SubStats2.Values.ToList().ForEach(subStat => StatsUtil.UpdateCalculations(subStat, indStats)); } }); } }
private void ComputeTankingStats(GenerateStatsOptions options) { lock (TankingGroupIds) { CombinedStats combined = null; Dictionary <string, PlayerStats> individualStats = new Dictionary <string, PlayerStats>(); if (RaidTotals != null) { // always start over RaidTotals.Total = 0; try { FireChartEvent(options, "UPDATE"); if (options.RequestSummaryData) { TankingGroups.ForEach(group => { group.ForEach(block => { block.Actions.ForEach(action => { if (action is DamageRecord record) { if (options.DamageType == 0 || (options.DamageType == 1 && IsMelee(record)) || (options.DamageType == 2 && !IsMelee(record))) { RaidTotals.Total += record.Total; PlayerStats stats = StatsUtil.CreatePlayerStats(individualStats, record.Defender); StatsUtil.UpdateStats(stats, record); PlayerSubStats subStats = StatsUtil.CreatePlayerSubStats(stats.SubStats, record.SubType, record.Type); StatsUtil.UpdateStats(subStats, record); } } }); }); }); RaidTotals.DPS = (long)Math.Round(RaidTotals.Total / RaidTotals.TotalSeconds, 2); Parallel.ForEach(individualStats.Values, stats => { StatsUtil.UpdateAllStatsTimeRanges(stats, PlayerTimeRanges, PlayerSubTimeRanges); 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 (Exception ex) { LOG.Error(ex); } if (options.RequestSummaryData) { // generating new stats var genEvent = new StatsGenerationEvent() { Type = Labels.TANKPARSE, State = "COMPLETED", CombinedStats = combined }; genEvent.Groups.AddRange(TankingGroups); genEvent.UniqueGroupCount = TankingGroupIds.Count; EventsGenerationStatus?.Invoke(this, genEvent); } } } }
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>(); ConcurrentDictionary <string, PlayerStats> aggregateStats = 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> >(); DamageGroups.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) { PlayerStats 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; StatsUtil.UpdateStats(stats, record); 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); 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); // dont count misses 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); } } } }
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 { FireUpdateEvent(options); if (options.RequestSummaryData) { HealingGroups.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 HealRecord record) { RaidTotals.Total += record.Total; PlayerStats stats = StatsUtil.CreatePlayerStats(individualStats, record.Healer); StatsUtil.UpdateStats(stats, record, block.BeginTime); allStats[record.Healer] = stats; var spellStatName = record.SubType ?? Labels.SELFHEAL; PlayerSubStats spellStats = StatsUtil.CreatePlayerSubStats(stats.SubStats, spellStatName, record.Type); StatsUtil.UpdateStats(spellStats, record, block.BeginTime); allStats[stats.Name + "=" + spellStatName] = spellStats; var healedStatName = record.Healed; PlayerSubStats healedStats = StatsUtil.CreatePlayerSubStats(stats.SubStats2, healedStatName, record.Type); StatsUtil.UpdateStats(healedStats, record, block.BeginTime); allStats[stats.Name + "=" + healedStatName] = healedStats; } }); }); 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), " 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; } } } catch (ArgumentNullException ane) { LOG.Error(ane); } catch (NullReferenceException nre) { LOG.Error(nre); } catch (ArgumentOutOfRangeException aro) { LOG.Error(aro); } FireCompletedEvent(options, combined, HealingGroups); } } }
internal void PopulateHealing(CombinedStats combined) { lock (HealingGroups) { List <PlayerStats> playerStats = combined.StatsList; Dictionary <string, PlayerStats> individualStats = new Dictionary <string, PlayerStats>(); Dictionary <string, long> totals = new Dictionary <string, long>(); HealingGroups.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 HealRecord record) { PlayerStats stats = StatsUtil.CreatePlayerStats(individualStats, record.Healed); StatsUtil.UpdateStats(stats, record, block.BeginTime); allStats[record.Healed] = stats; PlayerSubStats subStats = StatsUtil.CreatePlayerSubStats(stats.SubStats, record.Healer, record.Type); StatsUtil.UpdateStats(subStats, record, block.BeginTime); allStats[record.Healer + "-" + record.Healed] = subStats; var spellStatName = record.SubType ?? Labels.SELFHEAL; PlayerSubStats spellStats = StatsUtil.CreatePlayerSubStats(stats.SubStats2, spellStatName, record.Type); StatsUtil.UpdateStats(spellStats, record, block.BeginTime); allStats[stats.Name + "=" + spellStatName] = spellStats; long value = 0; if (totals.ContainsKey(record.Healed)) { value = totals[record.Healed]; } totals[record.Healed] = record.Total + value; } }); }); foreach (var stats in allStats.Values) { stats.TotalSeconds += stats.LastTime - stats.BeginTime + 1; stats.BeginTime = double.NaN; } }); Parallel.ForEach(playerStats, stat => { if (individualStats.ContainsKey(stat.Name)) { if (totals.ContainsKey(stat.Name)) { stat.Extra = totals[stat.Name]; } var indStat = individualStats[stat.Name]; stat.SubStats2["receivedHealing"] = indStat; StatsUtil.UpdateCalculations(indStat, RaidTotals); indStat.SubStats.Values.ToList().ForEach(subStat => StatsUtil.UpdateCalculations(subStat, indStat)); indStat.SubStats2.Values.ToList().ForEach(subStat => StatsUtil.UpdateCalculations(subStat, indStat)); } }); } }
private void ComputeDamageStats(GenerateStatsOptions options) { lock (DamageGroups) { 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>(); ConcurrentDictionary <string, PlayerStats> aggregateStats = new ConcurrentDictionary <string, PlayerStats>(); Dictionary <string, PlayerStats> individualStats = new Dictionary <string, PlayerStats>(); // always start over RaidTotals.Total = 0; try { FireUpdateEvent(options); if (options.RequestSummaryData) { DamageGroups.ForEach(group => { // keep track of time range as well as the players that have been updated Dictionary <string, PlayerSubStats> allStats = new Dictionary <string, PlayerSubStats>(); int found = -1; if (MainWindow.IsIgnoreIntialPullDamageEnabled) { // ignore initial low activity time double previousDps = 0; long rolling = 0; for (int i = 0; group.Count >= 10 && i < 10; i++) { if (previousDps == 0) { rolling = group[i].Actions.Sum(test => (test as DamageRecord).Total); previousDps = rolling / 1.0; } else { double theTime = group[i].BeginTime - group[0].BeginTime + 1; if (theTime > 12.0) { break; } rolling += group[i].Actions.Sum(test => (test as DamageRecord).Total); double currentDps = rolling / (theTime); if (currentDps / previousDps > 1.75) { found = i - 1; break; } else { previousDps = currentDps; } } } } var goodGroups = found > -1 ? group.GetRange(found, group.Count - found) : group; goodGroups.ForEach(block => { block.Actions.ForEach(action => { if (action is DamageRecord record) { PlayerStats 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; StatsUtil.UpdateStats(stats, record, block.BeginTime); allStats[record.Attacker] = stats; if (!PetToPlayer.TryGetValue(record.Attacker, out string player) && !PlayerHasPet.ContainsKey(record.Attacker)) { // not a pet topLevelStats[record.Attacker] = stats; } else { string origName = player ?? record.Attacker; string aggregateName = (player == Labels.UNASSIGNED) ? origName : origName + " +Pets"; PlayerStats aggregatePlayerStats = StatsUtil.CreatePlayerStats(individualStats, aggregateName, origName); StatsUtil.UpdateStats(aggregatePlayerStats, record, block.BeginTime); allStats[aggregateName] = aggregatePlayerStats; topLevelStats[aggregateName] = aggregatePlayerStats; if (!childrenStats.TryGetValue(aggregateName, out Dictionary <string, PlayerStats> children)) { childrenStats[aggregateName] = new Dictionary <string, PlayerStats>(); } childrenStats[aggregateName][stats.Name] = stats; } PlayerSubStats subStats = StatsUtil.CreatePlayerSubStats(stats.SubStats, record.SubType, record.Type); UpdateSubStats(subStats, record, block.BeginTime); allStats[stats.Name + "=" + Helpers.CreateRecordKey(record.Type, 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); // add up resists Dictionary <string, uint> resistCounts = new Dictionary <string, uint>(); Resists.ForEach(resist => { ResistRecord record = resist as ResistRecord; Helpers.StringUIntAddHelper.Add(resistCounts, record.Spell, 1); }); // get special field var specials = StatsUtil.GetSpecials(RaidTotals); Parallel.ForEach(individualStats.Values, stats => { if (topLevelStats.TryGetValue(stats.Name, out PlayerStats topLevel)) { if (childrenStats.TryGetValue(stats.Name, out Dictionary <string, PlayerStats> children)) { foreach (var child in children.Values) { StatsUtil.UpdateCalculations(child, RaidTotals, resistCounts); if (stats.Total > 0) { child.Percent = Math.Round(Convert.ToDouble(child.Total) / stats.Total * 100, 2); } if (specials.TryGetValue(child.Name, out string special1)) { child.Special = special1; } } } StatsUtil.UpdateCalculations(stats, RaidTotals, resistCounts); if (specials.TryGetValue(stats.OrigName, out string special2)) { stats.Special = special2; } } }); 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), " Damage ", StatsUtil.FormatTotals(RaidTotals.DPS)) }; combined.StatsList.AddRange(topLevelStats.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; if (childrenStats.TryGetValue(combined.StatsList[i].Name, out Dictionary <string, PlayerStats> children)) { combined.Children.Add(combined.StatsList[i].Name, children.Values.OrderByDescending(stats => stats.Total).ToList()); } } } } catch (ArgumentNullException anx) { LOG.Error(anx); } catch (AggregateException agx) { LOG.Error(agx); } FireCompletedEvent(options, combined, DamageGroups); } } }