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); } } } }
internal void BuildTotalStats(GenerateStatsOptions options) { lock (HealingGroups) { try { FireNewStatsEvent(options); Reset(); Selected = options.Npcs.OrderBy(sel => sel.Id).ToList(); 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.MaxTime = 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> >(); double currentTime = double.NaN; Dictionary <string, HashSet <string> > currentSpellCounts = new Dictionary <string, HashSet <string> >(); Dictionary <double, Dictionary <string, HashSet <string> > > previousSpellCounts = new Dictionary <double, Dictionary <string, HashSet <string> > >(); Dictionary <string, byte> ignoreRecords = new Dictionary <string, byte>(); List <ActionBlock> filtered = new List <ActionBlock>(); DataManager.Instance.GetHealsDuring(segment.BeginTime, segment.EndTime).ForEach(heal => { // copy var newBlock = new ActionBlock { BeginTime = heal.BeginTime }; filtered.Add(newBlock); if (currentSpellCounts.Count > 0) { previousSpellCounts[currentTime] = currentSpellCounts; } currentTime = heal.BeginTime; currentSpellCounts = new Dictionary <string, HashSet <string> >(); foreach (var timeKey in previousSpellCounts.Keys.ToList()) { if (previousSpellCounts.ContainsKey(timeKey)) { if (currentTime != double.NaN && (currentTime - timeKey) > 7) { previousSpellCounts.Remove(timeKey); } } } foreach (var record in heal.Actions.Cast <HealRecord>()) { if (PlayerManager.Instance.IsPetOrPlayer(record.Healed) || PlayerManager.Instance.IsPossiblePlayerName(record.Healed)) { // if AOEHealing is disabled then filter out AEs if (!MainWindow.IsAoEHealingEnabled) { SpellData spellData; if (record.SubType != null && (spellData = DataManager.Instance.GetHealingSpellByName(record.SubType)) != null) { if (spellData.Target == (byte)SpellTarget.TARGETAE || spellData.Target == (byte)SpellTarget.NEARBYPLAYERSAE || spellData.Target == (byte)SpellTarget.TARGETRINGAE || spellData.Target == (byte)SpellTarget.CASTERPBPLAYERS) { // just skip these entirely if AOEs are turned off continue; } else if ((spellData.Target == (byte)SpellTarget.CASTERGROUP || spellData.Target == (byte)SpellTarget.TARGETGROUP) && spellData.Mgb) { // need to count group AEs and if more than 6 are seen we need to ignore those // casts since they're from MGB and count as an AE var key = record.Healer + "|" + record.SubType; if (!currentSpellCounts.TryGetValue(key, out HashSet <string> value)) { value = new HashSet <string>(); currentSpellCounts[key] = value; } value.Add(record.Healed); HashSet <string> totals = new HashSet <string>(); List <double> temp = new List <double>(); foreach (var timeKey in previousSpellCounts.Keys) { if (previousSpellCounts[timeKey].ContainsKey(key)) { foreach (var item in previousSpellCounts[timeKey][key]) { totals.Add(item); } temp.Add(timeKey); } } foreach (var item in currentSpellCounts[key]) { totals.Add(item); } if (totals.Count > 6) { ignoreRecords[heal.BeginTime + "|" + key] = 1; temp.ForEach(timeKey => { ignoreRecords[timeKey + "|" + key] = 1; }); } } } } newBlock.Actions.Add(record); } } }); filtered.ForEach(heal => { var updatedHeal = new ActionBlock() { BeginTime = heal.BeginTime }; foreach (var record in heal.Actions.Cast <HealRecord>()) { var ignoreKey = heal.BeginTime + "|" + record.Healer + "|" + record.SubType; if (!ignoreRecords.ContainsKey(ignoreKey)) { 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"); } } catch (Exception ex) { LOG.Error(ex); } } }