private void AddRow(SpellDamageStats stats, string type) { var row = new ExpandoObject() as IDictionary <string, object>; row["Caster"] = stats.Caster; row["Spell"] = stats.Spell; row["Max"] = stats.Max; row["Hits"] = stats.Count; row["Avg"] = stats.Total / stats.Count; row["Total"] = stats.Total; row["Type"] = type; lock (LockObject) { Records.Add(row); } }
private void Load() { string selectedSpell = spellList.SelectedItem as string; string selectedPlayer = playerList.SelectedItem as string; bool isPlayerOnly = showPlayers.IsChecked.Value; Records.Clear(); Spells.Clear(); Spells.Add("All Spells"); Players.Clear(); Players.Add("All Casters"); var playerDDTotals = new Dictionary <string, SpellDamageStats>(); var playerDoTTotals = new Dictionary <string, SpellDamageStats>(); var uniqueSpells = new Dictionary <string, byte>(); var uniquePlayers = new Dictionary <string, byte>(); var fights = fightOption.SelectedIndex == 0 ? (Application.Current.MainWindow as MainWindow).GetFightTable()?.GetFights() : (Application.Current.MainWindow as MainWindow).GetFightTable()?.GetSelectedFights(); foreach (var fight in fights) { foreach (var kv in fight.DDDamage) { if (!isPlayerOnly || PlayerManager.Instance.IsVerifiedPlayer(kv.Value.Caster)) { if (!playerDDTotals.TryGetValue(kv.Key, out SpellDamageStats ddStats)) { ddStats = new SpellDamageStats { Caster = kv.Value.Caster, Spell = kv.Value.Spell }; playerDDTotals[kv.Key] = ddStats; uniqueSpells[kv.Value.Spell] = 1; uniquePlayers[kv.Value.Caster] = 1; } ddStats.Max = Math.Max(ddStats.Max, kv.Value.Max); ddStats.Total += kv.Value.Total; ddStats.Count += kv.Value.Count; } } foreach (var kv in fight.DoTDamage) { if (!isPlayerOnly || PlayerManager.Instance.IsVerifiedPlayer(kv.Value.Caster)) { if (!playerDoTTotals.TryGetValue(kv.Key, out SpellDamageStats dotStats)) { dotStats = new SpellDamageStats { Caster = kv.Value.Caster, Spell = kv.Value.Spell }; playerDoTTotals[kv.Key] = dotStats; uniqueSpells[kv.Value.Spell] = 1; uniquePlayers[kv.Value.Caster] = 1; } dotStats.Max = Math.Max(dotStats.Max, kv.Value.Max); dotStats.Total += kv.Value.Total; dotStats.Count += kv.Value.Count; } } } foreach (var stats in playerDoTTotals.Values) { AddRow(stats, Labels.DOT); } foreach (var stats in playerDDTotals.Values) { AddRow(stats, Labels.DD); } foreach (var key in uniqueSpells.Keys.OrderBy(k => k, StringComparer.Create(new CultureInfo("en-US"), true))) { Spells.Add(key); } foreach (var key in uniquePlayers.Keys.OrderBy(k => k, StringComparer.Create(new CultureInfo("en-US"), true))) { Players.Add(key); } spellList.SelectedIndex = (Spells.IndexOf(selectedSpell) is int s && s > -1) ? s : 0; playerList.SelectedIndex = (Players.IndexOf(selectedPlayer) is int p && p > -1) ? p : 0; if (dataGrid.ItemsSource is ListCollectionView view) { view.SortDescriptions.Clear(); view.SortDescriptions.Add(new SortDescription("Avg", ListSortDirection.Descending)); } titleLabel.Content = Records.Count == 0 ? NODATA : "Spell Damage Stats for " + uniqueSpells.Count + " Unique Spells"; }
private void HandleDamageProcessed(object sender, DamageProcessedEvent processed) { if (LastFightProcessTime != processed.BeginTime) { DataManager.Instance.CheckExpireFights(processed.BeginTime); ValidCombo.Clear(); if (processed.BeginTime - LastFightProcessTime > RECENTSPELLTIME) { RecentSpellCache.Clear(); } } // cache recent player spells to help determine who the caster was var isAttackerPlayer = PlayerManager.Instance.IsPetOrPlayer(processed.Record.Attacker) || processed.Record.Attacker == Labels.RS; if (isAttackerPlayer && (processed.Record.Type == Labels.DD || processed.Record.Type == Labels.DOT || processed.Record.Type == Labels.PROC)) { RecentSpellCache[processed.Record.SubType] = true; } string comboKey = processed.Record.Attacker + "=" + processed.Record.Defender; if (ValidCombo.TryGetValue(comboKey, out bool defender) || IsValidAttack(processed.Record, isAttackerPlayer, out defender)) { ValidCombo[comboKey] = defender; bool isNonTankingFight = false; string origTimeString = processed.OrigTimeString.Substring(4, 15); // fix for unknown spells having a good name to work from if (processed.Record.AttackerIsSpell && defender) { processed.Record.Attacker = Labels.UNK; } Fight fight = Get(processed.Record, processed.BeginTime, origTimeString, defender); if (defender) { Helpers.AddAction(fight.DamageBlocks, processed.Record, processed.BeginTime); AddPlayerTime(fight, processed.Record, processed.Record.Attacker, processed.BeginTime); fight.BeginDamageTime = double.IsNaN(fight.BeginDamageTime) ? processed.BeginTime : fight.BeginDamageTime; fight.LastDamageTime = processed.BeginTime; if (StatsUtil.IsHitType(processed.Record.Type)) { fight.DamageHits++; fight.Total += processed.Record.Total; isNonTankingFight = fight.DamageHits == 1; var attacker = processed.Record.AttackerOwner ?? processed.Record.Attacker; if (fight.PlayerTotals.TryGetValue(attacker, out FightTotalDamage total)) { total.Damage += (processed.Record.Type == Labels.BANE) ? 0 : processed.Record.Total; total.DamageWithBane += processed.Record.Total; total.Name = processed.Record.Attacker; total.PetOwner = total.PetOwner ?? processed.Record.AttackerOwner; total.UpdateTime = processed.BeginTime; } else { fight.PlayerTotals[attacker] = new FightTotalDamage { Damage = (processed.Record.Type == Labels.BANE) ? 0 : processed.Record.Total, DamageWithBane = processed.Record.Total, Name = processed.Record.Attacker, PetOwner = processed.Record.AttackerOwner, UpdateTime = processed.BeginTime, BeginTime = processed.BeginTime }; } SpellDamageStats stats = null; var spellKey = processed.Record.Attacker + "++" + processed.Record.SubType; if (processed.Record.Type == Labels.DD) { if (!fight.DDDamage.TryGetValue(spellKey, out stats)) { stats = new SpellDamageStats { Caster = processed.Record.Attacker, Spell = processed.Record.SubType }; fight.DDDamage[spellKey] = stats; } } else if (processed.Record.Type == Labels.DOT) { if (!fight.DoTDamage.TryGetValue(spellKey, out stats)) { stats = new SpellDamageStats { Caster = processed.Record.Attacker, Spell = processed.Record.SubType }; fight.DoTDamage[spellKey] = stats; } } if (stats != null) { stats.Count += 1; stats.Max = Math.Max(processed.Record.Total, stats.Max); stats.Total += processed.Record.Total; } // only a pet can 'hit' with a Flurry since players only crush/slash/punch/pierce with main hand weapons if (processed.Record.AttackerOwner == null && processed.Record.Type == Labels.MELEE && processed.Record.SubType == "Hits" && LineModifiersParser.IsFlurry(processed.Record.ModifiersMask)) { PlayerManager.Instance.AddVerifiedPet(processed.Record.Attacker); } } } else { Helpers.AddAction(fight.TankingBlocks, processed.Record, processed.BeginTime); AddPlayerTime(fight, processed.Record, processed.Record.Defender, processed.BeginTime); fight.BeginTankingTime = double.IsNaN(fight.BeginTankingTime) ? processed.BeginTime : fight.BeginTankingTime; fight.LastTankingTime = processed.BeginTime; if (StatsUtil.IsHitType(processed.Record.Type)) { fight.TankHits++; } } fight.LastTime = processed.BeginTime; LastFightProcessTime = processed.BeginTime; var ttl = fight.LastTime - fight.BeginTime + 1; fight.TooltipText = string.Format(CultureInfo.CurrentCulture, "#Hits To Players: {0}, #Hits From Players: {1}, Time Alive: {2}s", fight.TankHits, fight.DamageHits, ttl); DataManager.Instance.UpdateIfNewFightMap(fight.CorrectMapKey, fight, isNonTankingFight); } }