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));
                    }
                });
            }
        }
Example #2
0
 private void Reset()
 {
     PlayerTimeRanges.Clear();
     PlayerSubTimeRanges.Clear();
     TankingGroups.Clear();
     RaidTotals = StatsUtil.CreatePlayerStats(Labels.RAIDTOTALS);
     Selected   = null;
     Title      = "";
 }
 private void Reset()
 {
     HealedByHealerTimeRanges.Clear();
     HealedBySpellTimeRanges.Clear();
     HealerHealedTimeRanges.Clear();
     HealerSpellTimeRanges.Clear();
     HealingGroups.Clear();
     RaidTotals = StatsUtil.CreatePlayerStats(Labels.RAIDTOTALS);
     Selected   = null;
     Title      = "";
 }
        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 BuildTotalStats(GenerateStatsOptions options)
        {
            lock (HealingGroups)
            {
                Selected = options.Npcs;
                Title    = options.Name;

                try
                {
                    FireNewStatsEvent(options);

                    RaidTotals = StatsUtil.CreatePlayerStats(Labels.RAID);
                    HealingGroups.Clear();

                    Selected.ForEach(npc => StatsUtil.UpdateTimeDiffs(RaidTotals, npc, HEAL_OFFSET));
                    RaidTotals.TotalSeconds = RaidTotals.TimeDiffs.Sum();

                    if (RaidTotals.BeginTimes.Count > 0 && RaidTotals.BeginTimes.Count == RaidTotals.LastTimes.Count)
                    {
                        for (int i = 0; i < RaidTotals.BeginTimes.Count; i++)
                        {
                            var updatedHeals = new List <ActionBlock>();
                            var heals        = DataManager.Instance.GetHealsDuring(RaidTotals.BeginTimes[i], RaidTotals.LastTimes[i]);
                            heals.ForEach(heal =>
                            {
                                var updatedHeal = new ActionBlock()
                                {
                                    BeginTime = heal.BeginTime
                                };
                                updatedHeal.Actions.AddRange(heal.Actions.Where(item => item is HealRecord record && IsValidHeal(record)));

                                if (updatedHeal.Actions.Count > 0)
                                {
                                    updatedHeals.Add(updatedHeal);
                                }
                            });

                            if (updatedHeals.Count > 0)
                            {
                                HealingGroups.Add(updatedHeals);
                            }
                        }

                        ComputeHealingStats(options);
                    }
                    else if (Selected == null || Selected.Count == 0)
                    {
                        FireNoDataEvent(options, "NONPC");
                    }
                    else
                    {
                        FireNoDataEvent(options, "NODATA");
                    }
                }
                catch (ArgumentNullException ne)
                {
                    LOG.Error(ne);
                }
                catch (NullReferenceException nr)
                {
                    LOG.Error(nr);
                }
                catch (ArgumentOutOfRangeException aor)
                {
                    LOG.Error(aor);
                }
                catch (ArgumentException ae)
                {
                    LOG.Error(ae);
                }
                catch (OutOfMemoryException oem)
                {
                    LOG.Error(oem);
                }
            }
        }
        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));
                    }
                });
            }
        }
        internal void BuildTotalStats(GenerateStatsOptions options)
        {
            lock (DamageGroups)
            {
                Selected = options.Npcs;
                Title    = options.Name;

                try
                {
                    FireNewStatsEvent(options);

                    RaidTotals = StatsUtil.CreatePlayerStats(Labels.RAID);
                    DamageGroups.Clear();
                    DamageGroupIds.Clear();
                    PlayerHasPet.Clear();
                    PetToPlayer.Clear();
                    Resists.Clear();

                    var damageBlocks = new List <ActionBlock>();
                    Selected.ForEach(fight =>
                    {
                        StatsUtil.UpdateTimeDiffs(RaidTotals, fight);
                        damageBlocks.AddRange(fight.DamageBlocks);

                        if (fight.GroupId > -1)
                        {
                            DamageGroupIds[fight.GroupId] = 1;
                        }
                    });

                    damageBlocks.Sort((a, b) => a.BeginTime.CompareTo(b.BeginTime));

                    if (damageBlocks.Count > 0)
                    {
                        RaidTotals.TotalSeconds = RaidTotals.TimeDiffs.Sum();

                        var newBlock  = new List <ActionBlock>();
                        var timeIndex = 0;

                        damageBlocks.ForEach(block =>
                        {
                            if (block.BeginTime > RaidTotals.LastTimes[timeIndex])
                            {
                                timeIndex++;

                                if (newBlock.Count > 0)
                                {
                                    DamageGroups.Add(newBlock);
                                }

                                newBlock = new List <ActionBlock>();
                            }

                            newBlock.Add(block);

                            block.Actions.ForEach(action =>
                            {
                                DamageRecord damage = action as DamageRecord;
                                // see if there's a pet mapping, check this first
                                string pname = PlayerManager.Instance.GetPlayerFromPet(damage.Attacker);
                                if (!string.IsNullOrEmpty(pname) || !string.IsNullOrEmpty(pname = damage.AttackerOwner))
                                {
                                    PlayerHasPet[pname]          = 1;
                                    PetToPlayer[damage.Attacker] = pname;
                                }
                            });
                        });

                        DamageGroups.Add(newBlock);

                        for (int i = 0; i < RaidTotals.BeginTimes.Count && i < RaidTotals.LastTimes.Count; i++)
                        {
                            var group = DataManager.Instance.GetResistsDuring(RaidTotals.BeginTimes[i], RaidTotals.LastTimes[i]);
                            group.ForEach(block => Resists.AddRange(block.Actions));
                        }

                        ComputeDamageStats(options);
                    }
                    else if (Selected == null || Selected.Count == 0)
                    {
                        FireNoDataEvent(options, "NONPC");
                    }
                    else
                    {
                        FireNoDataEvent(options, "NODATA");
                    }
                }
                catch (ArgumentNullException ne)
                {
                    LOG.Error(ne);
                }
                catch (NullReferenceException nr)
                {
                    LOG.Error(nr);
                }
                catch (ArgumentOutOfRangeException aor)
                {
                    LOG.Error(aor);
                }
                catch (ArgumentException ae)
                {
                    LOG.Error(ae);
                }
                catch (OutOfMemoryException oem)
                {
                    LOG.Error(oem);
                }
            }
        }
        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);
        }
Example #14
0
        internal void BuildTotalStats(GenerateStatsOptions options)
        {
            lock (TankingGroups)
            {
                Selected = options.Npcs;
                Title    = options.Name;

                try
                {
                    FireNewStatsEvent(options);

                    RaidTotals = StatsUtil.CreatePlayerStats(Labels.RAID);
                    TankingGroups.Clear();

                    var damageBlocks = new List <ActionBlock>();
                    Selected.ForEach(fight =>
                    {
                        StatsUtil.UpdateTimeDiffs(RaidTotals, fight);
                        damageBlocks.AddRange(fight.TankingBlocks);
                    });

                    damageBlocks.Sort((a, b) => a.BeginTime.CompareTo(b.BeginTime));

                    if (damageBlocks.Count > 0)
                    {
                        RaidTotals.TotalSeconds = RaidTotals.TimeDiffs.Sum();

                        var newBlock  = new List <ActionBlock>();
                        var timeIndex = 0;

                        damageBlocks.ForEach(block =>
                        {
                            if (block.BeginTime > RaidTotals.LastTimes[timeIndex])
                            {
                                timeIndex++;

                                if (newBlock.Count > 0)
                                {
                                    TankingGroups.Add(newBlock);
                                }

                                newBlock = new List <ActionBlock>();
                            }

                            newBlock.Add(block);
                        });

                        TankingGroups.Add(newBlock);
                        ComputeTankingStats(options);
                    }
                    else if (Selected == null || Selected.Count == 0)
                    {
                        FireNoDataEvent(options, "NONPC");
                    }
                    else
                    {
                        FireNoDataEvent(options, "NODATA");
                    }
                }
                catch (ArgumentNullException ne)
                {
                    LOG.Error(ne);
                }
                catch (NullReferenceException nr)
                {
                    LOG.Error(nr);
                }
                catch (ArgumentOutOfRangeException aor)
                {
                    LOG.Error(aor);
                }
                catch (ArgumentException ae)
                {
                    LOG.Error(ae);
                }
                catch (OutOfMemoryException oem)
                {
                    LOG.Error(oem);
                }
            }
        }