public StatsSummary BuildSummary(string type, CombinedStats currentStats, List <PlayerStats> selected, bool showTotals, bool rankPlayers, bool _) { List <string> list = new List <string>(); string title = ""; string details = ""; if (currentStats != null && type == Labels.HEALPARSE) { if (selected?.Count > 0) { foreach (PlayerStats stats in selected.OrderByDescending(item => item.Total)) { string playerFormat = rankPlayers ? string.Format(CultureInfo.CurrentCulture, StatsUtil.PLAYER_RANK_FORMAT, stats.Rank, stats.Name) : string.Format(CultureInfo.CurrentCulture, StatsUtil.PLAYER_FORMAT, stats.Name); string damageFormat = string.Format(CultureInfo.CurrentCulture, StatsUtil.TOTAL_ONLY_FORMAT, StatsUtil.FormatTotals(stats.Total)); list.Add(playerFormat + damageFormat + " "); } } details = list.Count > 0 ? ", " + string.Join(" | ", list) : ""; title = StatsUtil.FormatTitle(currentStats.TargetTitle, currentStats.TimeTitle, showTotals ? currentStats.TotalTitle : ""); } return(new StatsSummary() { Title = title, RankedPlayers = details, }); }
private void UpdateTimerTick(object sender, EventArgs e) { lock (StatsLock) { try { Topmost = true; // possible workaround // people wanted shorter delays for damage updates but I don't want the indicator to change constantly // so this limits it to 1/2 the current time value ProcessDirection = !ProcessDirection; var timeout = CurrentDamageSelectionMode == 0 ? DataManager.FIGHTTIMEOUT : CurrentDamageSelectionMode; if (Stats == null || (DateTime.Now - DateTime.MinValue.AddSeconds(Stats.LastTime)).TotalSeconds > timeout) { windowBrush.Opacity = 0.0; ButtonPopup.IsOpen = false; SetVisible(false); Height = 0; Stats = null; PrevList = null; UpdateTimer.Stop(); } else if (Active && Stats != null) { var list = Stats.StatsList.Take(MAX_ROWS).ToList(); if (list.Count > 0) { TitleBlock.Text = Stats.TargetTitle; TitleDamageBlock.Text = string.Format(CultureInfo.CurrentCulture, "{0} [{1}s @{2}]", StatsUtil.FormatTotals(Stats.RaidStats.Total), Stats.RaidStats.TotalSeconds, StatsUtil.FormatTotals(Stats.RaidStats.DPS)); long total = 0; int goodRowCount = 0; long me = 0; var topList = new Dictionary <int, long>(); for (int i = 0; i < MAX_ROWS; i++) { if (list.Count > i) { if (ProcessDirection) { DamageRateList[i].Opacity = 0.0; } if (i == 0) { total = list[i].Total; RectangleList[i].Width = Width; } else { RectangleList[i].Visibility = Visibility.Hidden; // maybe it calculates width better RectangleList[i].Width = Convert.ToDouble(list[i].Total) / total * Width; } string playerName = ConfigUtil.PlayerName; var isMe = !string.IsNullOrEmpty(playerName) && list[i].Name.StartsWith(playerName, StringComparison.OrdinalIgnoreCase) && (playerName.Length >= list[i].Name.Length || list[i].Name[playerName.Length] == ' '); string updateText; if (IsHideOverlayOtherPlayersEnabled && !isMe) { updateText = string.Format(CultureInfo.CurrentCulture, "{0}. Hidden Player", list[i].Rank); } else { updateText = string.Format(CultureInfo.CurrentCulture, "{0}. {1}", list[i].Rank, list[i].Name); } if (IsShowOverlayCritRateEnabled) { List <string> critMods = new List <string>(); if (isMe && PlayerManager.Instance.IsDoTClass(list[i].ClassName) && DataManager.Instance.MyDoTCritRateMod is uint doTCritRate && doTCritRate > 0) { critMods.Add(string.Format(CultureInfo.CurrentCulture, "DoT CR +{0}", doTCritRate)); } if (isMe && DataManager.Instance.MyNukeCritRateMod is uint nukeCritRate && nukeCritRate > 0) { critMods.Add(string.Format(CultureInfo.CurrentCulture, "Nuke CR +{0}", nukeCritRate)); } if (critMods.Count > 0) { updateText = string.Format(CultureInfo.CurrentCulture, "{0} [{1}]", updateText, string.Join(", ", critMods)); } } NameBlockList[i].Text = updateText; if (i <= 4 && !isMe && list[i].Total > 0) { topList[i] = list[i].Total; } else if (isMe) { me = list[i].Total; } var damage = StatsUtil.FormatTotals(list[i].Total) + " [" + list[i].TotalSeconds + "s @" + StatsUtil.FormatTotals(list[i].DPS) + "]"; DamageBlockList[i].Text = damage; goodRowCount++; } } if (ProcessDirection) { if (me > 0 && topList.Count > 0) { var updatedList = new Dictionary <int, double>(); foreach (int i in topList.Keys) { if (i != me) { var diff = topList[i] / (double)me; updatedList[i] = diff; if (PrevList != null && PrevList.ContainsKey(i)) { if (PrevList[i] > diff) { DamageRateList[i].Icon = FontAwesomeIcon.LongArrowDown; DamageRateList[i].Foreground = DOWNBRUSH; DamageRateList[i].Opacity = DATA_OPACITY; } else if (PrevList[i] < diff) { DamageRateList[i].Icon = FontAwesomeIcon.LongArrowUp; DamageRateList[i].Foreground = UPBRUSH; DamageRateList[i].Opacity = DATA_OPACITY; } } } } PrevList = updatedList; } else { PrevList = null; } } var requested = (goodRowCount + 1) * CalculatedRowHeight; if (ActualHeight != requested) { Height = requested; } if (overlayCanvas.Visibility != Visibility.Visible) { overlayCanvas.Visibility = Visibility.Hidden; TitleRectangle.Visibility = Visibility.Hidden; TitlePanel.Visibility = Visibility.Hidden; TitleDamagePanel.Visibility = Visibility.Hidden; TitleRectangle.Height = CalculatedRowHeight; TitleDamagePanel.Height = CalculatedRowHeight; TitlePanel.Height = CalculatedRowHeight; overlayCanvas.Visibility = Visibility.Visible; TitleRectangle.Visibility = Visibility.Visible; TitlePanel.Visibility = Visibility.Visible; TitleDamagePanel.Visibility = Visibility.Visible; windowBrush.Opacity = OPACITY; ButtonPopup.IsOpen = true; } for (int i = 0; i < MAX_ROWS; i++) { SetRowVisible(i < goodRowCount, i); } } } } #pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) #pragma warning restore CA1031 // Do not catch general exception types { LOG.Error("Overlay Error", ex); } } }
public StatsSummary BuildSummary(string type, CombinedStats currentStats, List <PlayerStats> selected, bool showTotals, bool rankPlayers, bool _, bool showTime) { List <string> list = new List <string>(); string title = ""; string details = ""; if (currentStats != null) { if (type == Labels.TANKPARSE) { if (selected?.Count > 0) { foreach (PlayerStats stats in selected.OrderByDescending(item => item.Total)) { string playerFormat = rankPlayers ? string.Format(CultureInfo.CurrentCulture, StatsUtil.PLAYER_RANK_FORMAT, stats.Rank, stats.Name) : string.Format(CultureInfo.CurrentCulture, StatsUtil.PLAYER_FORMAT, stats.Name); string damageFormat = string.Format(CultureInfo.CurrentCulture, StatsUtil.TOTAL_ONLY_FORMAT, StatsUtil.FormatTotals(stats.Total)); list.Add(playerFormat + damageFormat + " "); } } details = list.Count > 0 ? ", " + string.Join(" | ", list) : ""; var timeTitle = showTime ? (" " + currentStats.TimeTitle) : ""; title = StatsUtil.FormatTitle(currentStats.TargetTitle, timeTitle, showTotals ? currentStats.TotalTitle : ""); } else if (type == Labels.RECEIVEDHEALPARSE) { if (selected?.Count == 1 && (selected[0] as PlayerStats).SubStats2.TryGetValue("receivedHealing", out PlayerSubStats subStats) && subStats is PlayerStats receivedHealing) { int rank = 1; long totals = 0; foreach (var stats in receivedHealing.SubStats.Values.OrderByDescending(stats => stats.Total).Take(10)) { string playerFormat = rankPlayers ? string.Format(CultureInfo.CurrentCulture, StatsUtil.PLAYER_RANK_FORMAT, rank++, stats.Name) : string.Format(CultureInfo.CurrentCulture, StatsUtil.PLAYER_FORMAT, stats.Name); string damageFormat = string.Format(CultureInfo.CurrentCulture, StatsUtil.TOTAL_ONLY_FORMAT, StatsUtil.FormatTotals(stats.Total)); list.Add(playerFormat + damageFormat + " "); totals += stats.Total; } var hps = (long)Math.Round(totals / currentStats.RaidStats.TotalSeconds, 2); string totalTitle = showTotals ? (selected[0].Name + " Received " + StatsUtil.FormatTotals(totals) + " Healing") : (selected[0].Name + " Received Healing"); details = list.Count > 0 ? ", " + string.Join(" | ", list) : ""; var timeTitle = showTime ? (" " + currentStats.TimeTitle) : ""; title = StatsUtil.FormatTitle(currentStats.TargetTitle, timeTitle, totalTitle); } } } return(new StatsSummary() { Title = title, RankedPlayers = details, }); }
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); } } }
public StatsSummary BuildSummary(string type, CombinedStats currentStats, List <PlayerStats> selected, bool showTotals, bool rankPlayers, bool showSpecial) { List <string> list = new List <string>(); string title = ""; string details = ""; if (currentStats != null && type == Labels.DAMAGEPARSE) { if (selected?.Count > 0) { foreach (PlayerStats stats in selected.OrderByDescending(item => item.Total)) { string playerFormat = rankPlayers ? string.Format(CultureInfo.CurrentCulture, StatsUtil.PLAYER_RANK_FORMAT, stats.Rank, stats.Name) : string.Format(CultureInfo.CurrentCulture, StatsUtil.PLAYER_FORMAT, stats.Name); string damageFormat = string.Format(CultureInfo.CurrentCulture, StatsUtil.TOTAL_FORMAT, StatsUtil.FormatTotals(stats.Total), "", StatsUtil.FormatTotals(stats.DPS)); string timeFormat = string.Format(CultureInfo.CurrentCulture, StatsUtil.TIME_FORMAT, stats.TotalSeconds); var dps = playerFormat + damageFormat + " " + timeFormat; if (showSpecial && !string.IsNullOrEmpty(stats.Special)) { dps = string.Format(CultureInfo.CurrentCulture, StatsUtil.SPECIAL_FORMAT, dps, stats.Special); } list.Add(dps); } } details = list.Count > 0 ? ", " + string.Join(", ", list) : ""; title = StatsUtil.FormatTitle(currentStats.TargetTitle, currentStats.TimeTitle, showTotals ? currentStats.TotalTitle : ""); } return(new StatsSummary() { Title = title, RankedPlayers = details }); }
private void ComputeTankingStats(GenerateStatsOptions options) { lock (TankingGroups) { 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) { 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; } } } #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 AggregateException || ex is NullReferenceException || ex is OutOfMemoryException) { LOG.Error(ex); } } if (options.RequestSummaryData) { // generating new stats var genEvent = new StatsGenerationEvent() { Type = Labels.TANKPARSE, State = "COMPLETED", CombinedStats = combined }; genEvent.Groups.AddRange(TankingGroups); EventsGenerationStatus?.Invoke(this, genEvent); } } } }
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); }
public StatsSummary BuildSummary(string type, CombinedStats currentStats, List <PlayerStats> selected, bool showTotals, bool rankPlayers, bool _, bool showTime, string customTitle) { List <string> list = new List <string>(); string title = ""; string details = ""; if (currentStats != null) { if (type == Labels.HEALPARSE) { if (selected?.Count > 0) { foreach (PlayerStats stats in selected.OrderByDescending(item => item.Total)) { string playerFormat = rankPlayers ? string.Format(CultureInfo.CurrentCulture, StatsUtil.PLAYER_RANK_FORMAT, stats.Rank, stats.Name) : string.Format(CultureInfo.CurrentCulture, StatsUtil.PLAYER_FORMAT, stats.Name); string healsFormat = string.Format(CultureInfo.CurrentCulture, StatsUtil.TOTAL_ONLY_FORMAT, StatsUtil.FormatTotals(stats.Total)); list.Add(playerFormat + healsFormat + " "); } } details = list.Count > 0 ? ", " + string.Join(" | ", list) : ""; var timeTitle = showTime ? (" " + currentStats.TimeTitle) : ""; title = StatsUtil.FormatTitle(customTitle ?? currentStats.TargetTitle, timeTitle, showTotals ? currentStats.TotalTitle : ""); } else if (type == Labels.TOPHEALSPARSE) { if (selected?.Count == 1 && selected[0].SubStats.Count > 0) { int rank = 1; foreach (var stats in selected[0].SubStats.Values.OrderByDescending(stats => stats.Total).Take(10)) { string abbrv = DataManager.Instance.AbbreviateSpellName(stats.Name); string playerFormat = rankPlayers ? string.Format(CultureInfo.CurrentCulture, StatsUtil.PLAYER_RANK_FORMAT, rank++, abbrv) : string.Format(CultureInfo.CurrentCulture, StatsUtil.PLAYER_FORMAT, abbrv); string healsFormat = string.Format(CultureInfo.CurrentCulture, StatsUtil.TOTAL_ONLY_FORMAT, StatsUtil.FormatTotals(stats.Total)); list.Add(playerFormat + healsFormat + " "); } string totalTitle = selected[0].Name + "'s Top Heals"; details = list.Count > 0 ? ", " + string.Join(" | ", list) : ""; var timeTitle = showTime ? currentStats.TimeTitle : ""; title = StatsUtil.FormatTitle(customTitle ?? currentStats.TargetTitle, timeTitle, totalTitle); } } } return(new StatsSummary() { Title = title, RankedPlayers = details, }); }
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; } } } catch (Exception ex) { 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 static CombinedStats ComputeOverlayStats(int mode, int maxRows, string selectedClass) { CombinedStats combined = null; var allDamage = 0L; var allTime = new TimeRange(); var playerTotals = new Dictionary <string, OverlayPlayerTotal>(); var playerHasPet = new Dictionary <string, bool>(); var updateTime = 0d; var oldestTime = 0d; var fights = DataManager.Instance.GetOverlayFights(); Fight oldestFight = null; bool baneEnabled = MainWindow.IsBaneDamageEnabled; // clear out anything pending in the queue DamageLineParser.CheckSlainQueue(DateUtil.ToDouble(DateTime.Now.AddSeconds(-3))); if (fights.Count > 0) { oldestFight = fights[0]; foreach (var fight in fights.Where(fight => !fight.Dead || mode > 0)) { foreach (var keypair in fight.PlayerTotals) { var player = keypair.Key; if (!string.IsNullOrEmpty(keypair.Value.PetOwner)) { player = keypair.Value.PetOwner; playerHasPet[player] = true; } else if (PlayerManager.Instance.GetPlayerFromPet(player) is string owner && owner != Labels.UNASSIGNED) { player = owner; playerHasPet[player] = true; } allDamage += baneEnabled ? keypair.Value.DamageWithBane : keypair.Value.Damage; allTime.Add(new TimeSegment(keypair.Value.BeginTime, fight.LastDamageTime)); if (updateTime == 0) { updateTime = keypair.Value.UpdateTime; oldestTime = keypair.Value.UpdateTime; oldestFight = fight; } else { updateTime = Math.Max(updateTime, keypair.Value.UpdateTime); if (oldestTime > keypair.Value.UpdateTime) { oldestTime = keypair.Value.UpdateTime; oldestFight = fight; } } if (playerTotals.TryGetValue(player, out OverlayPlayerTotal total)) { total.Damage += baneEnabled ? keypair.Value.DamageWithBane : keypair.Value.Damage; total.Range.Add(new TimeSegment(keypair.Value.BeginTime, keypair.Value.UpdateTime)); total.UpdateTime = Math.Max(total.UpdateTime, keypair.Value.UpdateTime); } else { playerTotals[player] = new OverlayPlayerTotal { Name = player, Damage = baneEnabled ? keypair.Value.DamageWithBane : keypair.Value.Damage, Range = new TimeRange(new TimeSegment(keypair.Value.BeginTime, keypair.Value.UpdateTime)), UpdateTime = keypair.Value.UpdateTime }; } } } var timeout = mode == 0 ? DataManager.FIGHTTIMEOUT : mode; var totalSeconds = allTime.GetTotal(); if (oldestFight != null && totalSeconds > 0 && allDamage > 0 && (DateTime.Now - DateTime.MinValue.AddSeconds(updateTime)).TotalSeconds <= timeout) { int rank = 1; var list = new List <PlayerStats>(); var totalDps = (long)Math.Round(allDamage / totalSeconds, 2); int myIndex = -1; foreach (var total in playerTotals.Values.OrderByDescending(total => total.Damage)) { var time = total.Range.GetTotal(); if (time > 0 && (DateTime.Now - DateTime.MinValue.AddSeconds(total.UpdateTime)).TotalSeconds <= DataManager.MAXTIMEOUT) { PlayerStats playerStats = new PlayerStats() { Name = playerHasPet.ContainsKey(total.Name) ? total.Name + " +Pets" : total.Name, Total = total.Damage, DPS = (long)Math.Round(total.Damage / time, 2), TotalSeconds = time, Rank = (ushort)rank++, ClassName = PlayerManager.Instance.GetPlayerClass(total.Name), OrigName = total.Name }; if (playerStats.Name.StartsWith(ConfigUtil.PlayerName, StringComparison.Ordinal)) { myIndex = list.Count; } if (myIndex == list.Count || selectedClass == Properties.Resources.ANY_CLASS || selectedClass == playerStats.ClassName) { list.Add(playerStats); } } } if (myIndex > (maxRows - 1)) { var me = list[myIndex]; list = list.Take(maxRows - 1).ToList(); list.Add(me); } else { list = list.Take(maxRows).ToList(); } combined = new CombinedStats(); combined.StatsList.AddRange(list); combined.RaidStats = new PlayerStats { Total = allDamage, DPS = totalDps, TotalSeconds = totalSeconds }; combined.TargetTitle = (fights.Count > 1 ? "C(" + fights.Count + "): " : "") + oldestFight.Name; // these are here to support copy/paste of the parse combined.TimeTitle = string.Format(CultureInfo.CurrentCulture, StatsUtil.TIME_FORMAT, combined.RaidStats.TotalSeconds); combined.TotalTitle = string.Format(CultureInfo.CurrentCulture, StatsUtil.TOTAL_FORMAT, StatsUtil.FormatTotals(combined.RaidStats.Total), " Damage ", StatsUtil.FormatTotals(combined.RaidStats.DPS)); } } return(combined); }