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 static void UpdateSubStats(PlayerSubStats subStats, DamageRecord record, double beginTime) { uint critHits = subStats.CritHits; StatsUtil.UpdateStats(subStats, record, beginTime); // 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 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); } } } }); }); });
internal void ComputeOverlayDamageStats(DamageRecord record, double beginTime, int timeout, OverlayDamageStats overlayStats = null) { try { // set current time overlayStats.LastTime = beginTime; if (record != null && (record.Type != Labels.BANE || MainWindow.IsBaneDamageEnabled)) { overlayStats.RaidStats.Total += record.Total; var raidTimeRange = new TimeRange(); overlayStats.InactiveFights.ForEach(fight => raidTimeRange.Add(new TimeSegment(Math.Max(fight.BeginDamageTime, overlayStats.BeginTime), fight.LastDamageTime))); overlayStats.ActiveFights.ForEach(fight => raidTimeRange.Add(new TimeSegment(Math.Max(fight.BeginDamageTime, overlayStats.BeginTime), fight.LastDamageTime))); overlayStats.RaidStats.TotalSeconds = Math.Max(raidTimeRange.GetTotal(), overlayStats.RaidStats.TotalSeconds); // update pets UpdatePetMapping(record); bool isPet = PetToPlayer.TryGetValue(record.Attacker, out string player); bool needAggregate = isPet || (!isPet && PlayerPets.ContainsKey(record.Attacker) && overlayStats.TopLevelStats.ContainsKey(record.Attacker + " +Pets")); if (!needAggregate) { // not a pet PlayerStats stats = StatsUtil.CreatePlayerStats(overlayStats.IndividualStats, record.Attacker); overlayStats.TopLevelStats[record.Attacker] = stats; StatsUtil.UpdateStats(stats, record); stats.LastTime = beginTime; } else { string origName = player ?? record.Attacker; string aggregateName = origName + " +Pets"; PlayerStats aggregatePlayerStats; aggregatePlayerStats = StatsUtil.CreatePlayerStats(overlayStats.IndividualStats, aggregateName, origName); overlayStats.TopLevelStats[aggregateName] = aggregatePlayerStats; if (overlayStats.TopLevelStats.ContainsKey(origName)) { var origPlayer = overlayStats.TopLevelStats[origName]; StatsUtil.MergeStats(aggregatePlayerStats, origPlayer); overlayStats.TopLevelStats.Remove(origName); overlayStats.IndividualStats.Remove(origName); } if (record.Attacker != origName && overlayStats.TopLevelStats.ContainsKey(record.Attacker)) { var origPet = overlayStats.TopLevelStats[record.Attacker]; StatsUtil.MergeStats(aggregatePlayerStats, origPet); overlayStats.TopLevelStats.Remove(record.Attacker); overlayStats.IndividualStats.Remove(record.Attacker); } StatsUtil.UpdateStats(aggregatePlayerStats, record); aggregatePlayerStats.LastTime = beginTime; } overlayStats.RaidStats.DPS = (long)Math.Round(overlayStats.RaidStats.Total / overlayStats.RaidStats.TotalSeconds, 2); var list = overlayStats.TopLevelStats.Values.OrderByDescending(item => item.Total).ToList(); int found = list.FindIndex(stats => stats.Name.StartsWith(ConfigUtil.PlayerName, StringComparison.Ordinal)); var you = found > -1 ? list[found] : null; int renumber; if (found > 4) { you.Rank = Convert.ToUInt16(found + 1); overlayStats.StatsList.Clear(); overlayStats.StatsList.AddRange(list.Where(stats => (stats != null && stats == you) || beginTime - stats.LastTime <= timeout).Take(4)); overlayStats.StatsList.Add(you); renumber = overlayStats.StatsList.Count - 1; } else { overlayStats.StatsList.Clear(); overlayStats.StatsList.AddRange(list.Where(stats => (stats != null && stats == you) || beginTime - stats.LastTime <= timeout).Take(5)); renumber = overlayStats.StatsList.Count; } for (int i = 0; i < overlayStats.StatsList.Count; i++) { if (i < renumber) { overlayStats.StatsList[i].Rank = Convert.ToUInt16(i + 1); } // only update time if damage changed if (overlayStats.StatsList[i].LastTime == beginTime && overlayStats.StatsList[i].CalcTime != beginTime) { var timeRange = new TimeRange(); if (PlayerPets.TryGetValue(overlayStats.StatsList[i].OrigName, out ConcurrentDictionary <string, byte> mapping)) { mapping.Keys.ToList().ForEach(key => { AddSegments(timeRange, overlayStats.InactiveFights, key, overlayStats.BeginTime); AddSegments(timeRange, overlayStats.ActiveFights, key, overlayStats.BeginTime); }); } AddSegments(timeRange, overlayStats.InactiveFights, overlayStats.StatsList[i].OrigName, overlayStats.BeginTime); AddSegments(timeRange, overlayStats.ActiveFights, overlayStats.StatsList[i].OrigName, overlayStats.BeginTime); overlayStats.StatsList[i].TotalSeconds = Math.Max(timeRange.GetTotal(), overlayStats.StatsList[i].TotalSeconds); overlayStats.StatsList[i].CalcTime = beginTime; } StatsUtil.UpdateCalculations(overlayStats.StatsList[i], overlayStats.RaidStats); } var count = overlayStats.InactiveFights.Count + overlayStats.ActiveFights.Count; overlayStats.TargetTitle = (count > 1 ? "C(" + count + "): " : "") + record.Defender; overlayStats.TimeTitle = string.Format(CultureInfo.CurrentCulture, StatsUtil.TIME_FORMAT, overlayStats.RaidStats.TotalSeconds); overlayStats.TotalTitle = string.Format(CultureInfo.CurrentCulture, StatsUtil.TOTAL_FORMAT, StatsUtil.FormatTotals(overlayStats.RaidStats.Total), " Damage ", StatsUtil.FormatTotals(overlayStats.RaidStats.DPS)); } } #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); } } void AddSegments(TimeRange range, List <Fight> fights, string key, double start) { fights.ForEach(fight => { if (fight.DamageSegments.TryGetValue(key, out TimeSegment segment) && segment.EndTime >= start) { range.Add(new TimeSegment(Math.Max(segment.BeginTime, start), segment.EndTime)); } }); } }
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); } } }
internal OverlayDamageStats ComputeOverlayDamageStats(DamageRecord record, double beginTime, OverlayDamageStats overlayStats = null) { if (overlayStats == null) { overlayStats = new OverlayDamageStats { RaidStats = new PlayerStats() }; overlayStats.RaidStats.BeginTime = beginTime; } else { overlayStats.RaidStats = overlayStats.RaidStats; } if (overlayStats.UniqueNpcs.Count == 0 || (beginTime - overlayStats.RaidStats.LastTime > DataManager.FIGHT_TIMEOUT)) { overlayStats.RaidStats.Total = 0; overlayStats.RaidStats.BeginTime = beginTime; overlayStats.UniqueNpcs.Clear(); overlayStats.TopLevelStats.Clear(); overlayStats.AggregateStats.Clear(); overlayStats.IndividualStats.Clear(); } overlayStats.RaidStats.LastTime = beginTime; overlayStats.RaidStats.TotalSeconds = overlayStats.RaidStats.LastTime - overlayStats.RaidStats.BeginTime + 1; if (record != null && (record.Type != Labels.BANE || MainWindow.IsBaneDamageEnabled)) { overlayStats.UniqueNpcs[record.Defender] = 1; overlayStats.RaidStats.Total += record.Total; // see if there's a pet mapping, check this first string pname = PlayerManager.Instance.GetPlayerFromPet(record.Attacker); if (pname != null || !string.IsNullOrEmpty(pname = record.AttackerOwner)) { PlayerHasPet[pname] = 1; PetToPlayer[record.Attacker] = pname; } bool isPet = PetToPlayer.TryGetValue(record.Attacker, out string player); bool needAggregate = isPet || (!isPet && PlayerHasPet.ContainsKey(record.Attacker) && overlayStats.TopLevelStats.ContainsKey(record.Attacker + " +Pets")); if (!needAggregate || player == Labels.UNASSIGNED) { // not a pet PlayerStats stats = StatsUtil.CreatePlayerStats(overlayStats.IndividualStats, record.Attacker); StatsUtil.UpdateStats(stats, record, beginTime); overlayStats.TopLevelStats[record.Attacker] = stats; stats.TotalSeconds = stats.LastTime - stats.BeginTime + 1; } else { string origName = player ?? record.Attacker; string aggregateName = origName + " +Pets"; PlayerStats aggregatePlayerStats; aggregatePlayerStats = StatsUtil.CreatePlayerStats(overlayStats.IndividualStats, aggregateName, origName); overlayStats.TopLevelStats[aggregateName] = aggregatePlayerStats; if (overlayStats.TopLevelStats.ContainsKey(origName)) { var origPlayer = overlayStats.TopLevelStats[origName]; StatsUtil.MergeStats(aggregatePlayerStats, origPlayer); overlayStats.TopLevelStats.Remove(origName); overlayStats.IndividualStats.Remove(origName); } if (record.Attacker != origName && overlayStats.TopLevelStats.ContainsKey(record.Attacker)) { var origPet = overlayStats.TopLevelStats[record.Attacker]; StatsUtil.MergeStats(aggregatePlayerStats, origPet); overlayStats.TopLevelStats.Remove(record.Attacker); overlayStats.IndividualStats.Remove(record.Attacker); } StatsUtil.UpdateStats(aggregatePlayerStats, record, beginTime); aggregatePlayerStats.TotalSeconds = aggregatePlayerStats.LastTime - aggregatePlayerStats.BeginTime + 1; } overlayStats.RaidStats.DPS = (long)Math.Round(overlayStats.RaidStats.Total / overlayStats.RaidStats.TotalSeconds, 2); var list = overlayStats.TopLevelStats.Values.OrderByDescending(item => item.Total).ToList(); int found = list.FindIndex(stats => stats.Name.StartsWith(ConfigUtil.PlayerName, StringComparison.Ordinal)); int renumber; if (found > 4) { var you = list[found]; you.Rank = Convert.ToUInt16(found + 1); overlayStats.StatsList.Clear(); overlayStats.StatsList.AddRange(list.Take(4)); overlayStats.StatsList.Add(you); renumber = overlayStats.StatsList.Count - 1; } else { overlayStats.StatsList.Clear(); overlayStats.StatsList.AddRange(list.Take(5)); renumber = overlayStats.StatsList.Count; } for (int i = 0; i < renumber; i++) { overlayStats.StatsList[i].Rank = Convert.ToUInt16(i + 1); } // only calculate the top few Parallel.ForEach(overlayStats.StatsList, top => StatsUtil.UpdateCalculations(top, overlayStats.RaidStats)); overlayStats.TargetTitle = (overlayStats.UniqueNpcs.Count > 1 ? "Combined (" + overlayStats.UniqueNpcs.Count + "): " : "") + record.Defender; overlayStats.TimeTitle = string.Format(CultureInfo.CurrentCulture, StatsUtil.TIME_FORMAT, overlayStats.RaidStats.TotalSeconds); overlayStats.TotalTitle = string.Format(CultureInfo.CurrentCulture, StatsUtil.TOTAL_FORMAT, StatsUtil.FormatTotals(overlayStats.RaidStats.Total), " Damage ", StatsUtil.FormatTotals(overlayStats.RaidStats.DPS)); } return(overlayStats); }
private static void UpdateSubStats(PlayerSubStats subStats, DamageRecord record, double beginTime) { StatsUtil.UpdateStats(subStats, record, beginTime); }