private void HealingSummary_SelectionChanged(object sender, PlayerStatsSelectionChangedEventArgs data) { var options = new GenerateStatsOptions() { RequestChartData = true }; HealingStatsManager.Instance.FireChartEvent(options, "SELECT", data.Selected); var preview = playerParseTextWindow.Content as ParsePreview; // change the update order based on whats displayed if (preview.parseList.SelectedItem?.ToString() == Labels.TOPHEALSPARSE) { preview?.UpdateParse(Labels.HEALPARSE, data.Selected); if (data.Selected?.Count == 1 && (data.Selected[0] as PlayerStats).SubStats?.Count > 0) { preview?.AddParse(Labels.TOPHEALSPARSE, HealingStatsManager.Instance, data.CurrentStats, data.Selected); } } else { if (data.Selected?.Count == 1 && (data.Selected[0] as PlayerStats).SubStats?.Count > 0) { preview?.AddParse(Labels.TOPHEALSPARSE, HealingStatsManager.Instance, data.CurrentStats, data.Selected); } preview?.UpdateParse(Labels.HEALPARSE, data.Selected); } }
private void OpenTankingSummary() { if (TankingWindow?.IsOpen == true) { TankingWindow.Close(); } else { #pragma warning disable CA2000 // Dispose objects before losing scope var tankingSummary = new TankingSummary(); #pragma warning restore CA2000 // Dispose objects before losing scope tankingSummary.EventsSelectionChange += TankingSummary_SelectionChanged; TankingWindow = new DocumentWindow(dockSite, "tankingSummary", "Tanking Summary", null, tankingSummary); IconToWindow[tankingSummaryIcon.Name] = TankingWindow; Helpers.OpenWindow(TankingWindow); if (DamageWindow?.IsOpen == true || HealingWindow?.IsOpen == true) { TankingWindow.MoveToPreviousContainer(); } RepositionCharts(TankingWindow); if (TankingStatsManager.Instance.GetGroupCount() > 0) { // keep chart request until resize issue is fixed. resetting the series fixes it at a minimum var tankingOptions = new GenerateStatsOptions() { RequestSummaryData = true }; Task.Run(() => TankingStatsManager.Instance.RebuildTotalStats(tankingOptions)); } } }
private void ComputeStats() { var filtered = (npcWindow?.Content as FightTable)?.GetSelectedItems().OrderBy(npc => npc.Id); string name = filtered?.FirstOrDefault()?.Name; var damageOptions = new GenerateStatsOptions() { Name = name, RequestChartData = DamageChartWindow?.IsOpen == true }; damageOptions.Npcs.AddRange(filtered); if (DamageWindow?.Content is DamageSummary damageSummary && DamageWindow?.IsOpen == true) { damageOptions.RequestSummaryData = true; } var healingOptions = new GenerateStatsOptions() { Name = name, RequestChartData = HealingChartWindow?.IsOpen == true }; healingOptions.Npcs.AddRange(filtered); if (HealingWindow?.Content is HealingSummary healingSummary && HealingWindow?.IsOpen == true) { healingOptions.RequestSummaryData = true; } var tankingOptions = new GenerateStatsOptions() { Name = name, RequestChartData = TankingChartWindow?.IsOpen == true }; tankingOptions.Npcs.AddRange(filtered); if (TankingWindow?.Content is TankingSummary tankingSummary && TankingWindow?.IsOpen == true) { tankingOptions.RequestSummaryData = true; } Task.Run(() => DamageStatsManager.Instance.BuildTotalStats(damageOptions)); Task.Run(() => HealingStatsManager.Instance.BuildTotalStats(healingOptions)); Task.Run(() => TankingStatsManager.Instance.BuildTotalStats(tankingOptions)); }
private void OpenHealingSummary() { if (HealingWindow?.IsOpen == true) { HealingWindow.Close(); } else { var healingSummary = new HealingSummary(); healingSummary.EventsSelectionChange += HealingSummary_SelectionChanged; HealingWindow = new DocumentWindow(dockSite, "healingSummary", "Healing Summary", null, healingSummary); IconToWindow[healingSummaryIcon.Name] = HealingWindow; Helpers.OpenWindow(HealingWindow); if (DamageWindow?.IsOpen == true || TankingWindow?.IsOpen == true) { HealingWindow.MoveToPreviousContainer(); } Helpers.RepositionCharts(HealingWindow, DamageChartWindow, TankingChartWindow, HealingChartWindow); if (HealingStatsManager.Instance.GetGroupCount() > 0) { // keep chart request until resize issue is fixed. resetting the series fixes it at a minimum var healingOptions = new GenerateStatsOptions() { RequestSummaryData = true }; Task.Run(() => HealingStatsManager.Instance.RebuildTotalStats(healingOptions)); } } }
internal void RebuildTotalStats(GenerateStatsOptions options) { if (TankingGroups.Count > 0) { FireNewStatsEvent(options); ComputeTankingStats(options); } }
internal void RebuildTotalStats(GenerateStatsOptions options) { if (DamageGroups.Count > 0) { FireNewStatsEvent(options); ComputeDamageStats(options); } }
private void OpenTankingChart() { if (OpenLineChart(TankingChartWindow, DamageChartWindow, HealingChartWindow, tankingChartIcon, "Tanking Chart", TANKING_CHOICES, false, out TankingChartWindow)) { var summary = TankingWindow?.Content as TankingSummary; var options = new GenerateStatsOptions() { RequestChartData = true }; TankingStatsManager.Instance.FireChartEvent(options, "UPDATE", summary?.GetSelectedStats(), summary?.GetFilter()); } }
private void OpenDamageChart() { if (OpenLineChart(DamageChartWindow, HealingChartWindow, TankingChartWindow, damageChartIcon, "Damage Chart", DAMAGE_CHOICES, true, out DamageChartWindow)) { var summary = DamageWindow?.Content as DamageSummary; var options = new GenerateStatsOptions() { RequestChartData = true }; DamageStatsManager.Instance.FireChartEvent(options, "UPDATE", summary?.GetSelectedStats(), summary?.GetFilter()); } }
private void ToggleBaneDamageClick(object sender, RoutedEventArgs e) { IsBaneDamageEnabled = !IsBaneDamageEnabled; ConfigUtil.SetSetting("IncludeBaneDamage", IsBaneDamageEnabled.ToString(CultureInfo.CurrentCulture)); enableBaneDamageIcon.Visibility = IsBaneDamageEnabled ? Visibility.Visible : Visibility.Hidden; var options = new GenerateStatsOptions() { RequestChartData = true, RequestSummaryData = true }; Task.Run(() => DamageStatsManager.Instance.RebuildTotalStats(options)); }
private void DamageSummary_SelectionChanged(object sender, PlayerStatsSelectionChangedEventArgs data) { var options = new GenerateStatsOptions() { RequestChartData = true }; DamageStatsManager.Instance.FireChartEvent(options, "SELECT", data.Selected); (playerParseTextWindow.Content as ParsePreview)?.UpdateParse(Labels.DAMAGEPARSE, data.Selected); }
private void FireNewStatsEvent(GenerateStatsOptions options) { if (options.RequestSummaryData) { // generating new stats EventsGenerationStatus?.Invoke(this, new StatsGenerationEvent() { Type = Labels.TANKPARSE, State = "STARTED" }); } }
private void FireNoDataEvent(GenerateStatsOptions options, string state) { if (options.RequestSummaryData) { // nothing to do EventsGenerationStatus?.Invoke(this, new StatsGenerationEvent() { Type = Labels.TANKPARSE, State = state }); } FireChartEvent(options, "CLEAR"); }
private void FireCompletedEvent(GenerateStatsOptions options, CombinedStats combined, List <List <ActionBlock> > groups) { if (options.RequestSummaryData) { // generating new stats var genEvent = new StatsGenerationEvent() { Type = Labels.HEALPARSE, State = "COMPLETED", CombinedStats = combined }; genEvent.Groups.AddRange(groups); EventsGenerationStatus?.Invoke(this, genEvent); } }
public DamageSummary() { InitializeComponent(); InitSummaryTable(title, dataGrid, selectedColumns); PropertyDescriptor widthPd = DependencyPropertyDescriptor.FromProperty(DataGridColumn.ActualWidthProperty, typeof(DataGridColumn)); PropertyDescriptor orderPd = DependencyPropertyDescriptor.FromProperty(DataGridColumn.DisplayIndexProperty, typeof(DataGridColumn)); foreach (var column in dataGrid.Columns) { widthPd.AddValueChanged(column, new EventHandler(ColumnWidthPropertyChanged)); orderPd.AddValueChanged(column, new EventHandler(ColumnDisplayIndexPropertyChanged)); } var list = PlayerManager.Instance.GetClassList(); list.Insert(0, Properties.Resources.ANY_CLASS); classesList.ItemsSource = list; classesList.SelectedIndex = 0; petOrPlayerList.ItemsSource = new List <string> { Labels.PETPLAYEROPTION, Labels.PLAYEROPTION, Labels.PETOPTION, Labels.EVERYTHINGOPTION }; petOrPlayerList.SelectedIndex = 0; CreateClassMenuItems(menuItemShowSpellCounts, DataGridShowSpellCountsClick, DataGridSpellCountsByClassClick); CreateClassMenuItems(menuItemShowSpellCasts, DataGridShowSpellCastsClick, DataGridSpellCastsByClassClick); CreateClassMenuItems(menuItemShowBreakdown, DataGridShowBreakdownClick, DataGridShowBreakdownByClassClick); DamageStatsManager.Instance.EventsGenerationStatus += Instance_EventsGenerationStatus; DataManager.Instance.EventsClearedActiveData += Instance_EventsClearedActiveData; SelectionTimer = new DispatcherTimer { Interval = new TimeSpan(0, 0, 0, 0, 1200) }; SelectionTimer.Tick += (sender, e) => { var damageOptions = new GenerateStatsOptions() { RequestSummaryData = true, MaxSeconds = timeChooser.Value }; Task.Run(() => DamageStatsManager.Instance.RebuildTotalStats(damageOptions)); SelectionTimer.Stop(); }; }
internal void RebuildTotalStats(GenerateStatsOptions options, bool updatedAoEOption = false) { if (HealingGroups.Count > 0) { if (updatedAoEOption) { var newOptions = new GenerateStatsOptions() { Name = Title, RequestChartData = options.RequestChartData, RequestSummaryData = options.RequestSummaryData }; newOptions.Npcs.AddRange(Selected); BuildTotalStats(newOptions); } else { FireNewStatsEvent(options); ComputeHealingStats(options); } } }
internal void FireChartEvent(GenerateStatsOptions options, string action, List <PlayerStats> selected = null, Predicate <object> filter = null) { lock (TankingGroupIds) { if (options.RequestChartData) { // send update DataPointEvent de = new DataPointEvent() { Action = action, Iterator = new TankGroupCollection(TankingGroups, options.DamageType), Filter = filter }; if (selected != null) { de.Selected.AddRange(selected); } EventsUpdateDataPoint?.Invoke(TankingGroups, de); } } }
internal void FireSelectionEvent(GenerateStatsOptions options, List <PlayerStats> selected) { FireChartEvent(options, "SELECT", selected); }
internal void BuildTotalStats(GenerateStatsOptions options) { lock (DamageGroupIds) { try { FireNewStatsEvent(options); Reset(); Selected = options.Npcs; Title = options.Name; var damageBlocks = new List <ActionBlock>(); Selected.ForEach(fight => { damageBlocks.AddRange(fight.DamageBlocks); if (fight.GroupId > -1) { DamageGroupIds[fight.GroupId] = 1; } RaidTotals.Ranges.Add(new TimeSegment(fight.BeginDamageTime, fight.LastDamageTime)); StatsUtil.UpdateRaidTimeRanges(fight, PlayerTimeRanges, PlayerSubTimeRanges); }); damageBlocks.Sort((a, b) => a.BeginTime.CompareTo(b.BeginTime)); if (damageBlocks.Count > 0) { RaidTotals.TotalSeconds = RaidTotals.Ranges.GetTotal(); RaidTotals.MaxTime = RaidTotals.TotalSeconds; int rangeIndex = 0; var newBlock = new List <ActionBlock>(); damageBlocks.ForEach(block => { if (RaidTotals.Ranges.TimeSegments.Count > rangeIndex && block.BeginTime > RaidTotals.Ranges.TimeSegments[rangeIndex].EndTime) { rangeIndex++; if (newBlock.Count > 0) { DamageGroups.Add(newBlock); } newBlock = new List <ActionBlock>(); } newBlock.Add(block); // update pet mapping block.Actions.ForEach(action => UpdatePetMapping(action as DamageRecord)); }); DamageGroups.Add(newBlock); RaidTotals.Ranges.TimeSegments.ForEach(segment => DataManager.Instance.GetResistsDuring(segment.BeginTime, segment.EndTime).ForEach(block => Resists.AddRange(block.Actions))); ComputeDamageStats(options); } else if (Selected == null || Selected.Count == 0) { FireNoDataEvent(options, "NONPC"); } else { FireNoDataEvent(options, "NODATA"); } } #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); } } } }
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 BuildTotalStats(GenerateStatsOptions options) { lock (TankingGroups) { try { FireNewStatsEvent(options); Reset(); Selected = options.Npcs; Title = options.Name; var damageBlocks = new List <ActionBlock>(); Selected.ForEach(fight => { damageBlocks.AddRange(fight.TankingBlocks); RaidTotals.Ranges.Add(new TimeSegment(fight.BeginTankingTime, fight.LastTankingTime)); StatsUtil.UpdateRaidTimeRanges(fight, PlayerTimeRanges, PlayerSubTimeRanges, true); }); damageBlocks.Sort((a, b) => a.BeginTime.CompareTo(b.BeginTime)); if (damageBlocks.Count > 0) { RaidTotals.TotalSeconds = RaidTotals.MaxTime = RaidTotals.Ranges.GetTotal(); int rangeIndex = 0; double lastTime = 0; var newBlock = new List <ActionBlock>(); damageBlocks.ForEach(block => { if (RaidTotals.Ranges.TimeSegments.Count > rangeIndex && block.BeginTime > RaidTotals.Ranges.TimeSegments[rangeIndex].EndTime) { rangeIndex++; if (newBlock.Count > 0) { TankingGroups.Add(newBlock); } newBlock = new List <ActionBlock>(); } if (lastTime != block.BeginTime) { var copy = new ActionBlock(); copy.Actions.AddRange(block.Actions); copy.BeginTime = block.BeginTime; newBlock.Add(copy); } else { newBlock.Last().Actions.AddRange(block.Actions); } }); TankingGroups.Add(newBlock); ComputeTankingStats(options); } else if (Selected == null || Selected.Count == 0) { FireNoDataEvent(options, "NONPC"); } else { FireNoDataEvent(options, "NODATA"); } } #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); } } } }
internal void BuildTotalStats(GenerateStatsOptions options) { lock (HealingGroups) { try { FireNewStatsEvent(options); Reset(); Selected = options.Npcs; Title = options.Name; Selected.ForEach(fight => RaidTotals.Ranges.Add(new TimeSegment(fight.BeginTankingTime, fight.LastTankingTime))); if (RaidTotals.Ranges.TimeSegments.Count > 0) { // calculate totals first since it can modify the ranges RaidTotals.TotalSeconds = RaidTotals.Ranges.GetTotal(); RaidTotals.Ranges.TimeSegments.ForEach(segment => { var updatedHeals = new List <ActionBlock>(); var healedByHealerTimeSegments = new Dictionary <string, Dictionary <string, TimeSegment> >(); var healedBySpellTimeSegments = new Dictionary <string, Dictionary <string, TimeSegment> >(); var healerHealedTimeSegments = new Dictionary <string, Dictionary <string, TimeSegment> >(); var healerSpellTimeSegments = new Dictionary <string, Dictionary <string, TimeSegment> >(); DataManager.Instance.GetHealsDuring(segment.BeginTime, segment.EndTime).ForEach(heal => { var updatedHeal = new ActionBlock() { BeginTime = heal.BeginTime }; foreach (var record in heal.Actions.Cast <HealRecord>()) { if (PlayerManager.Instance.IsPossiblePlayerName(record.Healed) || PlayerManager.Instance.IsPetOrPlayer(record.Healed)) { bool valid = true; SpellData spellData; if (record.SubType != null && (spellData = DataManager.Instance.GetSpellByName(record.SubType)) != null) { if (spellData.Target == (byte)SpellTarget.TARGETAE || spellData.Target == (byte)SpellTarget.NEARBYPLAYERSAE || spellData.Target == (byte)SpellTarget.TARGETRINGAE) { valid = MainWindow.IsAoEHealingEnabled; } } if (valid) { updatedHeal.Actions.Add(record); // store substats and substats2 which is based on the player that was healed var key = Helpers.CreateRecordKey(record.Type, record.SubType); StatsUtil.UpdateTimeSegments(null, healedByHealerTimeSegments, record.Healer, record.Healed, heal.BeginTime); StatsUtil.UpdateTimeSegments(null, healedBySpellTimeSegments, key, record.Healed, heal.BeginTime); StatsUtil.UpdateTimeSegments(null, healerHealedTimeSegments, record.Healed, record.Healer, heal.BeginTime); StatsUtil.UpdateTimeSegments(null, healerSpellTimeSegments, key, record.Healer, heal.BeginTime); } } } if (updatedHeal.Actions.Count > 0) { updatedHeals.Add(updatedHeal); } }); Parallel.ForEach(healedByHealerTimeSegments, kv => StatsUtil.AddSubTimeEntry(HealedByHealerTimeRanges, kv)); Parallel.ForEach(healedBySpellTimeSegments, kv => StatsUtil.AddSubTimeEntry(HealedBySpellTimeRanges, kv)); Parallel.ForEach(healerHealedTimeSegments, kv => StatsUtil.AddSubTimeEntry(HealerHealedTimeRanges, kv)); Parallel.ForEach(healerSpellTimeSegments, kv => StatsUtil.AddSubTimeEntry(HealerSpellTimeRanges, kv)); if (updatedHeals.Count > 0) { HealingGroups.Add(updatedHeals); } }); ComputeHealingStats(options); } else if (Selected == null || Selected.Count == 0) { FireNoDataEvent(options, "NONPC"); } else { FireNoDataEvent(options, "NODATA"); } } #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); } } } }
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); } } } }
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); } } }
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 FireFilterEvent(GenerateStatsOptions options, Predicate <object> filter) { FireChartEvent(options, "FILTER", null, filter); }
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); } } }
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); } } } }
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); } } }
internal void BuildTotalStats(GenerateStatsOptions options) { lock (TankingGroupIds) { try { FireNewStatsEvent(options); Reset(); Selected = options.Npcs.OrderBy(sel => sel.Id).ToList(); Title = options.Name; var damageBlocks = new List <ActionBlock>(); Selected.ForEach(fight => { damageBlocks.AddRange(fight.TankingBlocks); if (fight.GroupId > -1) { TankingGroupIds[fight.GroupId] = 1; } RaidTotals.Ranges.Add(new TimeSegment(fight.BeginTankingTime, fight.LastTankingTime)); StatsUtil.UpdateRaidTimeRanges(fight, PlayerTimeRanges, PlayerSubTimeRanges, true); }); damageBlocks.Sort((a, b) => a.BeginTime.CompareTo(b.BeginTime)); if (damageBlocks.Count > 0) { RaidTotals.TotalSeconds = RaidTotals.MaxTime = RaidTotals.Ranges.GetTotal(); int rangeIndex = 0; double lastTime = 0; var newBlock = new List <ActionBlock>(); damageBlocks.ForEach(block => { if (RaidTotals.Ranges.TimeSegments.Count > rangeIndex && block.BeginTime > RaidTotals.Ranges.TimeSegments[rangeIndex].EndTime) { rangeIndex++; if (newBlock.Count > 0) { TankingGroups.Add(newBlock); } newBlock = new List <ActionBlock>(); } if (lastTime != block.BeginTime) { var copy = new ActionBlock(); copy.Actions.AddRange(block.Actions); copy.BeginTime = block.BeginTime; newBlock.Add(copy); } else { newBlock.Last().Actions.AddRange(block.Actions); } }); TankingGroups.Add(newBlock); ComputeTankingStats(options); } else if (Selected == null || Selected.Count == 0) { FireNoDataEvent(options, "NONPC"); } else { FireNoDataEvent(options, "NODATA"); } } catch (Exception ex) { LOG.Error(ex); } } }
internal void FireUpdateEvent(GenerateStatsOptions options, List <PlayerStats> selected = null, Predicate <object> filter = null) { FireChartEvent(options, "UPDATE", selected, filter); }