From b30557805371440abbfcb0d1e8ec889d0481c898 Mon Sep 17 00:00:00 2001 From: bear-on-the-job Date: Thu, 9 May 2024 10:50:15 -0400 Subject: [PATCH] Combined the award processing into a single class, and cleaned up some of the usage. --- DiscordApi/Awards/Award.cs | 216 ++++++++++++++++- DiscordApi/Awards/AwardDisplay.cs | 227 ------------------ DiscordApi/Awards/AwardProcessor.cs | 23 -- .../DiscordApiJsonContentEmbedFooter.cs | 2 +- Forms/FormDiscordWebhooks.cs | 46 ++-- 5 files changed, 227 insertions(+), 287 deletions(-) delete mode 100644 DiscordApi/Awards/AwardDisplay.cs delete mode 100644 DiscordApi/Awards/AwardProcessor.cs diff --git a/DiscordApi/Awards/Award.cs b/DiscordApi/Awards/Award.cs index d79365d..f61b697 100644 --- a/DiscordApi/Awards/Award.cs +++ b/DiscordApi/Awards/Award.cs @@ -1,14 +1,220 @@ -namespace PlenBotLogUploader.DiscordApi.Awards +using PlenBotLogUploader.DpsReport.ExtraJson; +using System.Collections.Generic; +using System.Linq; + +namespace PlenBotLogUploader.DiscordApi.Awards { internal class Award { /// - /// Display values for the award + /// Primary key, description of the award /// - public AwardDisplay Display { get; set; } + public string Description { get; set; } /// - /// Qualifier and rank processor for the award + /// Name displayed in the discord report /// - public AwardProcessor Processor { get; set; } + public string Name { get; set; } + /// + /// Icon to display + /// + public string Emoji { get; set; } + /// + /// Rarity of the award (determines color) + /// One of: + /// "Common" + /// "Rare" + /// "Epic" + /// "Legendary" + /// + public string Rarity { get; set; } + /// + /// Whether the award should be displayed or not + /// + public string Active { get; set; } + /// + /// Whether the award should be displayed or not + /// + public string Category { get; set; } + /// + /// Whether the award should be displayed or not + /// + public string Property { get; set; } + /// + /// Whether the award should be displayed or not + /// + public float Qualifier { get; set; } + /// + /// Whether the award should be displayed or not + /// + public string[] AbilityNames { get; set; } + + /// + /// List of skills extracted from the JSON + /// + public IEnumerable Skills { get; set; } + /// + /// List of buffs extracted from the JSON + /// + public IEnumerable Buffs { get; set; } + + public bool Qualify(Player player, IEnumerable targets) + { + var result = Category switch + { + "Gameplay" => QualifyGameplay(player, targets), + "Offensive" => QualifyOffensive(player, targets), + "Defensive" => QualifyDefensive(player, targets), + "Support" => QualifySupport(player, targets), + "Skills" => QualifySkills(player, targets), + "Buffs" => QualifyBuffs(player, targets), + "DistanceToTag" => !player.IsCommander, + _ => false + }; + + return result; + } + + public float Rank(Player player, IEnumerable targets) + { + var result = Category switch + { + "Gameplay" => RankGameplay(player, targets), + "Offensive" => RankOffensive(player, targets), + "Defensive" => RankDefensive(player, targets), + "Support" => RankSupport(player, targets), + "Skills" => RankSkills(player, targets), + "Buffs" => RankBuffs(player, targets), + "DistanceToTag" => -player.StatsAll[0].DistToCom, + _ => 0f + }; + + return result; + } + + /// + /// Qualifies a player for the minimum score required + /// + private bool QualifyGameplay(Player player, IEnumerable targets) + => RankGameplay(player, targets) > Qualifier; + /// + /// Determines a player's score for the category + /// + private float RankGameplay(Player player, IEnumerable targets) + { + // Get the gameplay stats for the player + var gameplayStats = player.StatsAll?.FirstOrDefault(); + // Return the value based on property name + return float.TryParse(gameplayStats.GetType()?.GetProperty(Property)?.GetValue(gameplayStats).ToString(), out var result) ? result : 0f; + } + + /// + /// Qualifies a player for the minimum score required + /// + private bool QualifyOffensive(Player player, IEnumerable targets) => RankOffensive(player, targets) > Qualifier; + /// + /// Determines a player's score for the category + /// + private float RankOffensive(Player player, IEnumerable targets) + { + // Get the offensive stats for the player + var offensiveStats = player.StatsTargets + .Select(stats => stats?.FirstOrDefault()) + .Where(stats => stats is not null); + // Get the property from each stat matching the name, and get the value for that property as a float + var values = offensiveStats + .Select(stats => float.TryParse(stats.GetType()?.GetProperty(Property)?.GetValue(stats).ToString(), out var result) ? result : 0f) + .Where(value => value > 0f); + + // Sum up all the floats. + return values.Sum(); + } + + /// + /// Qualifies a player for the minimum score required + /// + private bool QualifyDefensive(Player player, IEnumerable targets) => RankDefensive(player, targets) > Qualifier; + /// + /// Determines a player's score for the category + /// + private float RankDefensive(Player player, IEnumerable targets) + { + // Get the gameplay stats for the player + var defensiveStats = player.Defenses?.FirstOrDefault(); + // Return the value based on property name + return float.TryParse(defensiveStats.GetType()?.GetProperty(Property)?.GetValue(defensiveStats).ToString(), out var result) ? result : 0f; + } + + /// + /// Qualifies a player for the minimum score required + /// + private bool QualifySupport(Player player, IEnumerable targets) => RankSupport(player, targets) > Qualifier; + /// + /// Determines a player's score for the category + /// + private float RankSupport(Player player, IEnumerable targets) + { + // Get the gameplay stats for the player + var supportStats = player.Support?.FirstOrDefault(); + // Return the value based on property name + return float.TryParse(supportStats.GetType()?.GetProperty(Property)?.GetValue(supportStats).ToString(), out var result) ? result : 0f; + } + + /// + /// Qualifies a player for the minimum score required + /// + private bool QualifySkills(Player player, IEnumerable targets) => RankSkills(player, targets) > Qualifier; + /// + /// Determines a player's score for the category + /// + private float RankSkills(Player player, IEnumerable targets) + { + // Create a list of skills, based on the skill names specified for this award. + var skills = AbilityNames?.Where(name => !string.IsNullOrEmpty(name))?.Any() is not true ? null : Skills + .Where(skill => AbilityNames.Any(skillName => skill.Name == skillName)) + .ToList(); + // Look through all target damage for the player, and get a list of skills that match the ones provided + var playerSkills = player.TargetDamageDist + .Where(damage => skills?.Any(skill => skill.Id == damage?.FirstOrDefault()?.FirstOrDefault()?.Id) ?? true) + .Select(damage => damage?.FirstOrDefault()?.FirstOrDefault()) + .Where(damage => damage is not null) + .ToList(); + // Get the property from each skill matching the name, and get the value for that property as a float + var values = playerSkills + .Select(skill => float.TryParse(skill.GetType()?.GetProperty(Property)?.GetValue(skill).ToString(), out var result) ? result : 0f) + .Where(value => value > 0f) + .ToList(); + + // Sum up all the floats. + return values.Sum(); + } + + /// + /// Qualifies a player for the minimum score required + /// + private bool QualifyBuffs(Player player, IEnumerable targets) => RankBuffs(player, targets) > Qualifier; + /// + /// Determines a player's score for the category + /// + private float RankBuffs(Player player, IEnumerable targets) + { + // Create a list of skills, based on the skill names specified for this award. + var buffs = AbilityNames?.Where(name => !string.IsNullOrEmpty(name))?.Any() is not true ? null : Buffs + .Where(buff => AbilityNames.Any(buffName => buff.Name == buffName)) + .ToList(); + // Look through all target damage for the player, and get a list of skills that match the ones provided + var playerBuffs = player.BuffUptimes + .Where(uptime => buffs?.Any(buff => buff.Id == uptime?.Id) ?? true) + .Select(uptime => uptime.BuffData?.FirstOrDefault()) + .Where(buffData => buffData is not null) + .ToList(); + // Get the property from each skill matching the name, and get the value for that property as a float + var values = playerBuffs + .Select(buffData => float.TryParse(buffData.GetType()?.GetProperty(Property)?.GetValue(buffData).ToString(), out var result) ? result : 0f) + .Where(value => value > 0f) + .ToList(); + + // Sum up all the floats. + return values.Sum(); + } } } diff --git a/DiscordApi/Awards/AwardDisplay.cs b/DiscordApi/Awards/AwardDisplay.cs deleted file mode 100644 index cb20b2f..0000000 --- a/DiscordApi/Awards/AwardDisplay.cs +++ /dev/null @@ -1,227 +0,0 @@ -using Hardstuck.GuildWars2.BuildCodes.V2; -using PlenBotLogUploader.DpsReport.ExtraJson; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace PlenBotLogUploader.DiscordApi.Awards -{ - internal class AwardDisplay - { - /// - /// Primary key, description of the award - /// - public string Description { get; set; } - /// - /// Name displayed in the discord report - /// - public string Name { get; set; } - /// - /// Icon to display - /// - public string Emoji { get; set; } - /// - /// Rarity of the award (determines color) - /// One of: - /// "Common" - /// "Rare" - /// "Epic" - /// "Legendary" - /// - public string Rarity { get; set; } - /// - /// Whether the award should be displayed or not - /// - public string Active { get; set; } - - /// - /// Whether the award should be displayed or not - /// - public string Category { get; set; } - - /// - /// Whether the award should be displayed or not - /// - public string Property { get; set; } - - /// - /// Whether the award should be displayed or not - /// - public float Qualifier { get; set; } - - /// - /// Whether the award should be displayed or not - /// - public string[] AbilityNames { get; set; } - - /// - /// List of skills extracted from the JSON - /// - public IEnumerable Skills { get; set; } - /// - /// List of buffs extracted from the JSON - /// - public IEnumerable Buffs { get; set; } - - - public bool Qualify(Player player, IEnumerable targets) - { - var result = Category switch - { - "Gameplay" => QualifyGameplay(player, targets), - "Offensive" => QualifyOffensive(player, targets), - "Defensive" => QualifyDefensive(player, targets), - "Support" => QualifySupport(player, targets), - "Skills" => QualifySkills(player, targets), - "Buffs" => QualifyBuffs(player, targets), - "DistanceToTag" => !player.IsCommander, - _ => false - }; - - return result; - } - - public float Rank(Player player, IEnumerable targets) - { - var result = Category switch - { - "Gameplay" => RankGameplay(player, targets), - "Offensive" => RankOffensive(player, targets), - "Defensive" => RankDefensive(player, targets), - "Support" => RankSupport(player, targets), - "Skills" => RankSkills(player, targets), - "Buffs" => RankBuffs(player, targets), - "DistanceToTag" => -player.StatsAll[0].DistToCom, - _ => 0f - }; - - return result; - } - - /// - /// Qualifies a player for the minimum score required - /// - private bool QualifyGameplay(Player player, IEnumerable targets) - => RankGameplay(player, targets) > Qualifier; - /// - /// Determines a player's score for the category - /// - private float RankGameplay(Player player, IEnumerable targets) - { - // Get the gameplay stats for the player - var gameplayStats = player.StatsAll?.FirstOrDefault(); - // Return the value based on property name - return float.TryParse(gameplayStats.GetType()?.GetProperty(Property)?.GetValue(gameplayStats).ToString(), out var result) ? result : 0f; - } - - /// - /// Qualifies a player for the minimum score required - /// - private bool QualifyOffensive(Player player, IEnumerable targets) => RankOffensive(player, targets) > Qualifier; - /// - /// Determines a player's score for the category - /// - private float RankOffensive(Player player, IEnumerable targets) - { - // Get the offensive stats for the player - var offensiveStats = player.StatsTargets - .Select(stats => stats?.FirstOrDefault()) - .Where(stats => stats is not null); - // Get the property from each stat matching the name, and get the value for that property as a float - var values = offensiveStats - .Select(stats => float.TryParse(stats.GetType()?.GetProperty(Property)?.GetValue(stats).ToString(), out var result) ? result : 0f) - .Where(value => value > 0f); - - // Sum up all the floats. - return values.Sum(); - } - - /// - /// Qualifies a player for the minimum score required - /// - private bool QualifyDefensive(Player player, IEnumerable targets) => RankDefensive(player, targets) > Qualifier; - /// - /// Determines a player's score for the category - /// - private float RankDefensive(Player player, IEnumerable targets) - { - // Get the gameplay stats for the player - var defensiveStats = player.Defenses?.FirstOrDefault(); - // Return the value based on property name - return float.TryParse(defensiveStats.GetType()?.GetProperty(Property)?.GetValue(defensiveStats).ToString(), out var result) ? result : 0f; - } - - /// - /// Qualifies a player for the minimum score required - /// - private bool QualifySupport(Player player, IEnumerable targets) => RankSupport(player, targets) > Qualifier; - /// - /// Determines a player's score for the category - /// - private float RankSupport(Player player, IEnumerable targets) - { - // Get the gameplay stats for the player - var supportStats = player.Support?.FirstOrDefault(); - // Return the value based on property name - return float.TryParse(supportStats.GetType()?.GetProperty(Property)?.GetValue(supportStats).ToString(), out var result) ? result : 0f; - } - - /// - /// Qualifies a player for the minimum score required - /// - private bool QualifySkills(Player player, IEnumerable targets) => RankSkills(player, targets) > Qualifier; - /// - /// Determines a player's score for the category - /// - private float RankSkills(Player player, IEnumerable targets) - { - // Create a list of skills, based on the skill names specified for this award. - var skills = AbilityNames?.Where(name => !string.IsNullOrEmpty(name))?.Any() is not true ? null : Skills - .Where(skill => AbilityNames.Any(skillName => skill.Name == skillName)) - .ToList(); - // Look through all target damage for the player, and get a list of skills that match the ones provided - var playerSkills = player.TargetDamageDist - .Where(damage => skills?.Any(skill => skill.Id == damage?.FirstOrDefault()?.FirstOrDefault()?.Id) ?? true) - .Select(damage => damage?.FirstOrDefault()?.FirstOrDefault()) - .Where(damage => damage is not null) - .ToList(); - // Get the property from each skill matching the name, and get the value for that property as a float - var values = playerSkills - .Select(skill => float.TryParse(skill.GetType()?.GetProperty(Property)?.GetValue(skill).ToString(), out var result) ? result : 0f) - .Where(value => value > 0f) - .ToList(); - - // Sum up all the floats. - return values.Sum(); - } - - /// - /// Qualifies a player for the minimum score required - /// - private bool QualifyBuffs(Player player, IEnumerable targets) => RankBuffs(player, targets) > Qualifier; - /// - /// Determines a player's score for the category - /// - private float RankBuffs(Player player, IEnumerable targets) - { - // Create a list of skills, based on the skill names specified for this award. - var buffs = AbilityNames?.Where(name => !string.IsNullOrEmpty(name))?.Any() is not true ? null : Buffs - .Where(buff => AbilityNames.Any(buffName => buff.Name == buffName)) - .ToList(); - // Look through all target damage for the player, and get a list of skills that match the ones provided - var playerBuffs = player.BuffUptimes - .Where(uptime => buffs?.Any(buff => buff.Id == uptime?.Id) ?? true) - .Select(uptime => uptime.BuffData?.FirstOrDefault()) - .Where(buffData => buffData is not null) - .ToList(); - // Get the property from each skill matching the name, and get the value for that property as a float - var values = playerBuffs - .Select(buffData => float.TryParse(buffData.GetType()?.GetProperty(Property)?.GetValue(buffData).ToString(), out var result) ? result : 0f) - .Where(value => value > 0f) - .ToList(); - - // Sum up all the floats. - return values.Sum(); - } - } -} diff --git a/DiscordApi/Awards/AwardProcessor.cs b/DiscordApi/Awards/AwardProcessor.cs deleted file mode 100644 index 00e642a..0000000 --- a/DiscordApi/Awards/AwardProcessor.cs +++ /dev/null @@ -1,23 +0,0 @@ -using PlenBotLogUploader.DpsReport.ExtraJson; -using System; -using System.Collections.Generic; - -namespace PlenBotLogUploader.DiscordApi.Awards -{ - internal class AwardProcessor - { - /// - /// Primary key, description of the award - /// - public string Description { get; set; } - /// - /// Determines if a player is qualified to win the award - /// - public Func, bool> Qualifier { get; set; } - /// - /// After qualifying, determines the ranking of the player compared to other qualifiers. - /// Higher number result means higher ranking. - /// - public Func, float> Ranking { get; set; } - } -} diff --git a/DiscordApi/DiscordApiJsonContentEmbedFooter.cs b/DiscordApi/DiscordApiJsonContentEmbedFooter.cs index 8f8f685..b6b3318 100644 --- a/DiscordApi/DiscordApiJsonContentEmbedFooter.cs +++ b/DiscordApi/DiscordApiJsonContentEmbedFooter.cs @@ -12,7 +12,7 @@ internal sealed class DiscordApiJsonContentEmbedFooter /// footer text /// [JsonProperty("text")] - internal string Text { get; set; } = $"PlenBot Log Uploader r.{ApplicationSettings.Version}.BEAR.3"; + internal string Text { get; set; } = $"PlenBot Log Uploader r.{ApplicationSettings.Version}.BEAR.5"; /// /// url of the footer icon (only supports http(s) and attachments) diff --git a/Forms/FormDiscordWebhooks.cs b/Forms/FormDiscordWebhooks.cs index 2cca48f..0fe4c24 100644 --- a/Forms/FormDiscordWebhooks.cs +++ b/Forms/FormDiscordWebhooks.cs @@ -39,15 +39,14 @@ public partial class FormDiscordWebhooks : Form Inline = false }; - private List awardDisplays = new List(); private List awards = new List(); /// /// Parses CSV content into award display list /// - private static List ParseCsvToAwardDisplays(string csvContent, IEnumerable skills, IEnumerable buffs) + private static List ParseCsvToAwardProcessors(string csvContent, IEnumerable skills, IEnumerable buffs) { - List awardDisplays = new List(); + List awardDisplays = new List(); // Split the CSV content into lines var lines = csvContent.Split("\r\n"); @@ -62,8 +61,8 @@ private static List ParseCsvToAwardDisplays(string csvContent, IEn var columns = lines[i].Split(','); if (columns.Length < 8) continue; - // Create a new Person object and add it to the list - awardDisplays.Add(new AwardDisplay + // Create a new award and add it to the list + awardDisplays.Add(new Award { Skills = skills, Buffs = buffs, @@ -101,7 +100,7 @@ private void LoadAwardDisplays(string googleSheetsUrl, IEnumerable skills var csvContent = client.GetStringAsync(csvUrl).Result; if (csvContent is null) return; - awardDisplays = ParseCsvToAwardDisplays(csvContent, skills, buffs); + awards = ParseCsvToAwardProcessors(csvContent, skills, buffs); } /// @@ -112,12 +111,8 @@ private IEnumerable AssignAwards(IEnumerable ad.Active.Equals("yes", StringComparison.InvariantCultureIgnoreCase) || ad.Active.Equals("true", StringComparison.InvariantCultureIgnoreCase)) - .Select(ad => new Award { Display = ad }) - //.Select(ad => new Award { Display = ad, Processor = awardProcessors.FirstOrDefault(ap => ap.Description == ad.Description) }) - //.Where(a => a.Processor is not null) - .ToList(); + // Only process active awards + var awards = this.awards.Where(ad => ad.Active.Equals("yes", StringComparison.InvariantCultureIgnoreCase) || ad.Active.Equals("true", StringComparison.InvariantCultureIgnoreCase)); // Run all awards to see which has most qualifiers var qualifiers = awards @@ -126,28 +121,20 @@ private IEnumerable AssignAwards(IEnumerable new { Award = award, - Players = players.Where(p => award.Display.Qualify(p, targets)).OrderByDescending(p => award.Display.Rank(p, targets)) + Players = players.Where(p => award.Qualify(p, targets)).OrderByDescending(p => award.Rank(p, targets)) } ) .Where(q => q.Players.Any()) .ToList(); - var shuffled = Shuffle(qualifiers).ToList(); - var legendary = shuffled - .Where(s => s.Award.Display.Rarity == "Legendary") - .ToList(); - var epic = shuffled - .Where(s => s.Award.Display.Rarity == "Epic") - .ToList(); - var rare = shuffled - .Where(s => s.Award.Display.Rarity == "Rare") - .ToList(); - var common = shuffled - .Where(s => s.Award.Display.Rarity == "Common") - .ToList(); + var shuffled = Shuffle(qualifiers).ToList(); + var legendary = shuffled.Where(s => s.Award.Rarity == "Legendary"); + var epic = shuffled.Where(s => s.Award.Rarity == "Epic"); + var rare = shuffled.Where(s => s.Award.Rarity == "Rare"); + var common = shuffled.Where(s => s.Award.Rarity == "Common"); // Default the winners to those with legendary awards - var winners = legendary; + var winners = legendary.ToList(); winners.AddRange(epic); winners.AddRange(rare); winners.AddRange(common); @@ -157,14 +144,11 @@ private IEnumerable AssignAwards(IEnumerable CreateAwardField(winner.Award.Display.Name, winner.Award.Display.Description, winner.Award.Display.Emoji, winner.Award.Display.Rarity, winner.Players.FirstOrDefault()) + winner => CreateAwardField(winner.Award.Name, winner.Award.Description, winner.Award.Emoji, winner.Award.Rarity, winner.Players.FirstOrDefault()) ) .ToList(); fields.Insert(0, FIELD_SPACER); - fields.Insert(0, FIELD_SPACER); - //fields.First().Name = "\n" + fields.First().Name; - return fields; }