private void AddToList(PlayerStats playerStats, List <PlayerSubStats> list, List <PlayerSubStats> additionalStats) { additionalStats.ForEach(stats => { if (list.Find(item => item.Name == stats.Name) is PlayerSubStats found) { var combined = new PlayerSubStats() { Name = found.Name }; StatsUtil.MergeStats(combined, found); StatsUtil.MergeStats(combined, stats); StatsUtil.CalculateRates(combined, RaidStats, playerStats); list.Remove(found); list.Add(combined); } else { list.Add(stats); } }); }
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 BuildGroups(PlayerStats playerStats, List <PlayerSubStats> all) { List <PlayerSubStats> list = new List <PlayerSubStats>(); PlayerSubStats dots = new PlayerSubStats() { Name = Labels.DOT, Type = Labels.DOT }; PlayerSubStats dds = new PlayerSubStats() { Name = Labels.DD, Type = Labels.DD }; PlayerSubStats procs = new PlayerSubStats() { Name = Labels.PROC, Type = Labels.PROC }; PlayerSubStats resisted = new PlayerSubStats() { Name = Labels.RESIST, Type = Labels.RESIST, ResistRate = 100 }; List <PlayerSubStats> allDots = new List <PlayerSubStats>(); List <PlayerSubStats> allDds = new List <PlayerSubStats>(); List <PlayerSubStats> allProcs = new List <PlayerSubStats>(); List <PlayerSubStats> allResisted = new List <PlayerSubStats>(); all.ForEach(sub => { PlayerSubStats stats = null; switch (sub.Type) { case Labels.DOT: stats = dots; allDots.Add(sub); break; case Labels.DD: case Labels.BANE: stats = dds; allDds.Add(sub); break; case Labels.PROC: stats = procs; allProcs.Add(sub); break; case Labels.RESIST: stats = resisted; allResisted.Add(sub); break; default: list.Add(sub); break; } StatsUtil.MergeStats(stats, sub); }); foreach (var stats in new PlayerSubStats[] { dots, dds, procs, resisted }) { StatsUtil.CalculateRates(stats, RaidStats, playerStats); } UnGroupedDD[playerStats.Name] = allDds; UnGroupedDoT[playerStats.Name] = allDots; UnGroupedProcs[playerStats.Name] = allProcs; UnGroupedResisted[playerStats.Name] = allResisted; GroupedDD[playerStats.Name] = dds; GroupedDoT[playerStats.Name] = dots; GroupedProcs[playerStats.Name] = procs; GroupedResisted[playerStats.Name] = resisted; OtherDamage[playerStats.Name] = list; Dispatcher.InvokeAsync(() => { if (allDds.Count > 0 && !groupDirectDamage.IsEnabled) { groupDirectDamage.IsEnabled = true; } if (allProcs.Count > 0 && !groupProcs.IsEnabled) { groupProcs.IsEnabled = true; } if (allDots.Count > 0 && !groupDoT.IsEnabled) { groupDoT.IsEnabled = true; } if (allResisted.Count > 0 && !groupResisted.IsEnabled) { groupResisted.IsEnabled = true; } }); }
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); }