public async Task TankerTank(CommandContext ctx, [Description("The *gamer tag* or *PSN Name*. If it has spaces, enclose it on quotes.")] string gamerTag, [Description("The Tank name, as it appears in battles. If it has spaces, enclose it on quotes.")][RemainingText] string tankName) { // Yeah... it's a Player feature but it uses more calculations for tanks... if (!await CanExecute(ctx, Features.Players)) { return; } if (string.IsNullOrWhiteSpace(gamerTag)) { await ctx.RespondAsync($"Please specify the *Gamer Tag*, {ctx.User.Mention}. Something like `!w tankerTank \"{ctx.User.Username.RemoveDiacritics()}\" \"HWK 12\"`, for example."); return; } if (string.IsNullOrWhiteSpace(tankName)) { await ctx.RespondAsync($"Please specify the *Tank Name*, {ctx.User.Mention}. Something like `!w tankerTank \"{ctx.User.Username.RemoveDiacritics()}\" \"HWK 12\"`, for example."); return; } await ctx.TriggerTypingAsync(); Log.Debug($"Requesting {nameof(TankerTank)}({gamerTag}, {tankName})..."); try { var provider = new DbProvider(_connectionString); var playerCommands = new PlayerCommands(); var player = await playerCommands.GetPlayer(ctx, gamerTag); if (player == null) { Log.Debug($"Could not find player {gamerTag}"); await ctx.RespondAsync("I could not find a tanker " + $"with the Gamer Tag `{gamerTag}` on the Database, {ctx.User.Mention}. I may not track this player, or the Gamer Tag is wrong."); return; } gamerTag = player.Name; var tank = FindTank(player.Plataform, tankName, out _); if (tank == null) { await ctx.RespondAsync($"Can't find a tank with `{tankName}` on the name, {ctx.User.Mention}."); return; } var tr = provider.GetTanksReferences(tank.Plataform, null, tank.TankId, false, false, false).FirstOrDefault(); if (tr == null) { await ctx.RespondAsync($"Sorry, there is no tank statistics for the `{tank.Name}`, {ctx.User.Mention}."); return; } var hist = provider.GetPlayerHistoryByTank(player.Plataform, player.Id, tank.TankId).ToArray(); if (!hist.Any()) { await ctx.RespondAsync($"Sorry, there is no tank statistics history for the `{tank.Name}` for the player `{gamerTag}`, {ctx.User.Mention}."); return; } var wn8Expected = provider.GetWn8ExpectedValues(player.Plataform); foreach (var h in hist) { h.Wn8 = wn8Expected.CalculateWn8(h); } TankPlayerStatistics[] head, tail; if (hist.Length > 20) { head = hist.Take(15).ToArray(); tail = hist.Skip(hist.Length - 5).Take(5).ToArray(); } else { head = hist; tail = null; } var sb = new StringBuilder(); sb.AppendLine($"History of the {Formatter.MaskedUrl(tr.Name, new Uri(tr.Url))}, Tier {tr.Tier.ToRomanNumeral()}, {tr.Nation.GetDescription()}, {(tr.IsPremium ? "Premium" : "Regular")}, as played by {Formatter.MaskedUrl(gamerTag, new Uri(player.PlayerOverallUrl))}, {ctx.User.Mention}:"); sb.AppendLine(); sb.AppendLine("```"); sb.AppendLine("Date Battles Tot.Dmg WN8"); foreach (var t in head) { sb.AppendLine($"{t.LastBattle:yyyy-MM-dd} {t.Battles.ToString("N0").PadLeft(7)} {t.TotalDamagePerBattle.ToString("N0").PadLeft(7)} {t.Wn8.ToString("N0").PadLeft(6)}"); } if (tail != null) { sb.AppendLine("..."); foreach (var t in tail) { sb.AppendLine($"{t.LastBattle:yyyy-MM-dd} {t.Battles.ToString("N0").PadLeft(7)} {t.TotalDamagePerBattle.ToString("N0").PadLeft(7)} {t.Wn8.ToString("N0").PadLeft(6)}"); } } sb.AppendLine("```"); sb.AppendLine(); var ptr = player.Performance.All[tr.TankId]; sb.AppendLine($"**{player.Name}** current values:"); sb.AppendLine($"Total Damage: {ptr.TotalDamagePerBattle:N0} "); sb.AppendLine($"Direct Damage: {ptr.DirectDamagePerBattle:N0} "); sb.AppendLine($"Assisted Damage: {ptr.DamageAssistedPerBattle:N0} "); sb.AppendLine($"WN8: {ptr.Wn8:N0}; Win Rate: {ptr.WinRate:P1}"); sb.AppendLine($"Battles: {ptr.Battles:N0}; Hours Battling: {ptr.BattleLifeTime.TotalHours:N0}"); sb.AppendLine($"Max Kills: {ptr.MaxFrags:N1}; Avg Kills: {ptr.KillsPerBattle:N1}"); if (tr.LastMonth != null) { sb.AppendLine(); sb.AppendLine($"**Global Recent** average among {tr.LastMonth.TotalPlayers:N0} players of the {tr.Name}:"); sb.AppendLine($"Total Damage: {tr.LastMonth.TotalDamage:N0} "); sb.AppendLine($"Direct Damage: {tr.LastMonth.DamageDealt:N0} "); sb.AppendLine($"Assisted Damage: {tr.LastMonth.DamageAssisted:N0} "); sb.AppendLine($"WN8: {tr.LastMonth.AverageWn8:N0}; Win Rate: {tr.LastMonth.WinRatio:P1}"); sb.AppendLine($"Battles: {tr.LastMonth.BattlesPerPlayer:N0}; Hours Battling: {tr.LastMonth.TimePerPlayer.TotalHours:N0}"); sb.AppendLine($"Max Kills: {tr.LastMonth.MaxKills:N1}; Avg Kills: {tr.LastMonth.Kills:N1}"); } var platformPrefix = tr.Plataform == Platform.PS ? "ps." : string.Empty; var color = ptr.Wn8.ToColor(); var embed = new DiscordEmbedBuilder { Title = $"{gamerTag} history with the {tank.Name}", Description = sb.ToString(), Color = new DiscordColor(color.R, color.G, color.B), ThumbnailUrl = tank.SmallImageUrl, Url = player.PlayerOverallUrl, Author = new DiscordEmbedBuilder.EmbedAuthor { Name = "WoTClans", Url = $"https://{platformPrefix}wotclans.com.br" }, Footer = new DiscordEmbedBuilder.EmbedFooter { Text = $"Calculated at {player.Moment:yyyy-MM-dd HH:mm} UTC" } }; await ctx.RespondAsync("", embed : embed); } catch (Exception ex) { Log.Error($"{nameof(TankerTank)}({gamerTag})", ex); await ctx.RespondAsync($"Sorry, {ctx.User.Mention}. There was an error... the *Coder* will be notified of `{ex.Message}`."); } }
public async Task RandTank(CommandContext ctx, [Description("The minimum tier of the tanks")] int minTier = 5, [Description("The maximum tier of the tanks")] int maxTier = 10, [Description("Nation of the tank, or *any*. Multiple values can be sent using *;* as separators")] string nationFilter = "any", [Description("Type of the tank, or *any*. Multiple values can be sent using *;* as separators")] string typeFilter = "any", [Description("*Premium*, *regular*, or *any*")] string premiumFilter = "any", [Description("The *gamer tag* or *PSN Name*, so it only returns tanks that the player have (or had)")] string gamerTag = null, [Description("If *true* then a tank that the given player **hadn't** will be picked")] bool notOnPlayer = false) { // Yeah... it's a Player feature but it uses more calculations for tanks... if (!await CanExecute(ctx, Features.Tanks)) { return; } if (minTier < 1) { await ctx.RespondAsync("The minimum tier is 1."); return; } if (minTier > 10) { await ctx.RespondAsync("The maximum tier is 1."); return; } if (maxTier < 1) { await ctx.RespondAsync("The minimum tier is 1."); return; } if (maxTier > 10) { await ctx.RespondAsync("The maximum tier is 1."); return; } if (maxTier < minTier) { var temp = maxTier; maxTier = minTier; minTier = temp; } await ctx.TriggerTypingAsync(); var cfg = GuildConfiguration.FromGuild(ctx.Guild); Log.Debug($"Requesting {nameof(RandTank)}({minTier}, {maxTier}, {nationFilter}, {typeFilter}, {premiumFilter}, {gamerTag ?? string.Empty}, {notOnPlayer})..."); try { var provider = new DbProvider(_connectionString); var allTanks = provider.EnumTanks(cfg.Plataform).Where(t => (t.Tier >= minTier) && (t.Tier <= maxTier)).ToList(); if (!string.IsNullOrWhiteSpace(nationFilter) && !nationFilter.EqualsCiAi("any")) { var filtersText = nationFilter.Split(new[] { ',', ';', '-', ' ' }, StringSplitOptions.RemoveEmptyEntries); var filters = new HashSet <Nation>(); foreach (var filterText in filtersText) { if (Enum.TryParse <Nation>(filterText, true, out var nation)) { filters.Add(nation); } else { await ctx.RespondAsync( $"Sorry, {ctx.User.Mention}, the nation `{filterText}` is not a valid nation. Valid nations are: {string.Join(", ", NationExtensions.GetGameNations().Select(n => $"`{n.ToString().ToLowerInvariant()}`"))}."); return; } } allTanks = allTanks.Where(t => filters.Contains(t.Nation)).ToList(); } if (!string.IsNullOrWhiteSpace(typeFilter) && !typeFilter.EqualsCiAi("any")) { var filtersText = typeFilter.Split(new[] { ',', ';', '-', ' ' }, StringSplitOptions.RemoveEmptyEntries); var filters = new HashSet <TankType>(); foreach (var filterText in filtersText) { if (Enum.TryParse <TankType>(filterText, true, out var tankType)) { filters.Add(tankType); } else { await ctx.RespondAsync( $"Sorry, {ctx.User.Mention}, the tank type `{filterText}` is not a valid type. Valid tank types are: {string.Join(", ", TankTypeExtensions.GetGameTankTypes().Select(n => $"`{n.ToString().ToLowerInvariant()}`"))}."); return; } } allTanks = allTanks.Where(t => filters.Contains(t.Type)).ToList(); } if (!string.IsNullOrWhiteSpace(premiumFilter) && !premiumFilter.EqualsCiAi("any")) { if (premiumFilter.EqualsCiAi("regular")) { allTanks = allTanks.Where(t => !t.IsPremium).ToList(); } else if (premiumFilter.EqualsCiAi("premium")) { allTanks = allTanks.Where(t => t.IsPremium).ToList(); } else { await ctx.RespondAsync( $"Sorry, {ctx.User.Mention}, the premium filter `{premiumFilter}` is not a valid value. Valid values are: `Regular`, `Premium` or `Any`."); return; } } if (!string.IsNullOrWhiteSpace(gamerTag)) { var playerCommands = new PlayerCommands(); var player = await playerCommands.GetPlayer(ctx, gamerTag); if (player == null) { Log.Debug($"Could not find player {gamerTag}"); await ctx.RespondAsync("I could not find a tanker " + $"with the Gamer Tag `{gamerTag}` on the Database, {ctx.User.Mention}. I may not track this player, or the Gamer Tag is wrong."); return; } gamerTag = player.Name; var playerTanks = player.Performance.All.Select(kv => kv.Key).ToHashSet(); allTanks = notOnPlayer ? allTanks.Where(t => !playerTanks.Contains(t.TankId)).ToList() : allTanks.Where(t => playerTanks.Contains(t.TankId)).ToList(); } if (allTanks.Count <= 0) { await ctx.RespondAsync( $"Sorry, {ctx.User.Mention}, there are no tanks with the given criteria."); return; } var number = Rand.Next(0, allTanks.Count); var tank = allTanks[number]; var platformPrefix = tank.Plataform == Platform.PS ? "ps." : string.Empty; var sb = new StringBuilder(); sb.AppendLine($"The random tank is the `{tank.Name}`, Tier {tank.Tier.ToRomanNumeral()}, {tank.Nation.GetDescription()}, {(tank.IsPremium ? "Premium" : "Regular")}, {ctx.User.Mention}!"); var embed = new DiscordEmbedBuilder { Title = $"{tank.Name} is the random Tank", Description = sb.ToString(), Color = DiscordColor.Gray, ThumbnailUrl = tank.SmallImageUrl, Url = $"https://{platformPrefix}wotclans.com.br/Tanks/{tank.TankId}", Author = new DiscordEmbedBuilder.EmbedAuthor { Name = "WoTClans", Url = $"https://{platformPrefix}wotclans.com.br" }, Footer = new DiscordEmbedBuilder.EmbedFooter { Text = $"Picked at {DateTime.UtcNow:yyyy-MM-dd HH:mm}." } }; await ctx.RespondAsync("", embed : embed); } catch (Exception ex) { Log.Error($"Error on {nameof(RandTank)}({minTier}, {maxTier}, {nationFilter}, {typeFilter}, {premiumFilter}, {gamerTag ?? string.Empty}, {notOnPlayer})", ex); await ctx.RespondAsync($"Sorry, {ctx.User.Mention}. There was an error... the *Coder* will be notified of `{ex.Message}`."); } }
public async Task ClanTopOnTank(CommandContext ctx, [Description("The clan **tag**")] string clanTag, [Description("The Tank name, as it appears in battles. If it has spaces, enclose it on quotes.")][RemainingText] string tankName) { if (!await CanExecute(ctx, Features.Clans)) { return; } await ctx.TriggerTypingAsync(); if (string.IsNullOrWhiteSpace(clanTag)) { await ctx.RespondAsync($"You must send a clan tag as parameter, {ctx.User.Mention}."); return; } Log.Debug($"Requesting {nameof(ClanTopOnTank)}({clanTag}, {tankName})..."); var cfg = GuildConfiguration.FromGuild(ctx.Guild); var platform = GetPlatform(clanTag, cfg.Plataform, out clanTag); if (!ClanTagRegex.IsMatch(clanTag)) { await ctx.RespondAsync($"You must send a **valid** clan **tag** as parameter, {ctx.User.Mention}."); return; } var provider = new DbProvider(_connectionString); var clan = provider.GetClan(platform, clanTag); if (clan == null) { platform = platform == Platform.PS ? Platform.XBOX : Platform.PS; clan = provider.GetClan(platform, clanTag); if (clan == null) { await ctx.RespondAsync( $"Can't find on a clan with tag `[{clanTag}]`, {ctx.User.Mention}. Maybe my site doesn't track it yet... or you have the wrong clan tag."); return; } } if (!clan.Enabled) { await ctx.RespondAsync( $"Data collection for the `[{clan.ClanTag}]` is disabled, {ctx.User.Mention}. Maybe the clan went too small, or inactive."); return; } var tankCommands = new TankCommands(); var tank = tankCommands.FindTank(platform, tankName, out _); if (tank == null) { await ctx.RespondAsync($"Can't find a tank with `{tankName}` on the name, {ctx.User.Mention}."); return; } var tr = provider.GetTanksReferences(tank.Plataform, null, tank.TankId, false, false, false).FirstOrDefault(); if (tr == null) { await ctx.RespondAsync($"Sorry, there is no tank statistics for the `{tank.Name}`, {ctx.User.Mention}."); return; } if (tr.Tier < 5) { await ctx.RespondAsync($"Sorry, this command is meant to be used only with tanks Tier 5 and above, {ctx.User.Mention}."); return; } var players = provider.GetClanPlayerIdsOnTank(platform, clan.ClanId, tr.TankId).ToList(); if (players.Count <= 0) { await ctx.RespondAsync($"No players from the `[{clan.ClanTag}]` has battles on the `{tank.Name}`, {ctx.User.Mention}, as far as the database is up to date."); return; } var waitMsg = await ctx.RespondAsync($"Please wait as data for {players.Count} tankers is being retrieved, {ctx.User.Mention}..."); var playerCommands = new PlayerCommands(); var fullPlayers = new ConcurrentBag <Player>(); var tasks = players.Select(async p => { var player = await playerCommands.GetPlayer(ctx, ((p.Plataform == Platform.XBOX) ? "x." : "ps.") + p.Name); if (player == null) { await ctx.RespondAsync($"Sorry, could not get updated information for player `{p.Name}`, {ctx.User.Mention}."); return; } fullPlayers.Add(player); }); await Task.WhenAll(tasks); await waitMsg.DeleteAsync(); var sb = new StringBuilder(); var maxNameLength = fullPlayers.Max(p => p.Name.Length); //sb.AppendLine($"Here `[{clan.ClanTag}]` top players on the `{tank.Name}`, {ctx.User.Mention}:"); //sb.AppendLine(); sb.AppendLine("```"); sb.AppendLine($"{platform.TagName().PadRight(maxNameLength)} {"Days".PadLeft(5)} {"Battles".PadLeft(7)} {"WN8".PadLeft(6)}"); foreach (var p in fullPlayers.OrderByDescending(p => p.Performance.All[tank.TankId].Wn8).Take(25)) { var tp = p.Performance.All[tank.TankId]; sb.AppendLine($"{(p.Name ?? string.Empty).PadRight(maxNameLength)} {(DateTime.UtcNow - tp.LastBattle).TotalDays.ToString("N0").PadLeft(5)} {tp.Battles.ToString("N0").PadLeft(7)} {tp.Wn8.ToString("N0").PadLeft(6)}"); } sb.AppendLine("```"); sb.AppendLine(); sb.AppendLine("This command is a **Premium** feature on the bot. For now it's free to use this command, but be advised that on the near future access will be restricted to Premium subscribers."); var color = clan.Top15Wn8.ToColor(); var platformPrefix = clan.Plataform == Platform.PS ? "ps." : string.Empty; var embed = new DiscordEmbedBuilder { Title = $"`{clan.ClanTag}` top players on the `{tank.Name}`", Description = sb.ToString(), Color = new DiscordColor(color.R, color.G, color.B), ThumbnailUrl = tank.SmallImageUrl, Url = $"https://{platformPrefix}wotclans.com.br/Clan/{clan.ClanTag}", Author = new DiscordEmbedBuilder.EmbedAuthor { Name = "WoTClans", Url = $"https://{platformPrefix}wotclans.com.br" }, Footer = new DiscordEmbedBuilder.EmbedFooter { Text = $"Calculated at {DateTime.UtcNow:yyyy-MM-dd HH:mm} UTC" } }; await ctx.RespondAsync("", embed : embed); }
public async Task TankerTank(CommandContext ctx, [Description("The *gamer tag* or *PSN Name*. If it has spaces, enclose it on quotes.")] string gamerTag, [Description("The Tank name, as it appears in battles. If it has spaces, enclose it on quotes.")] string tankName, [Description("Shows additional info explaining the calculated WN8")] bool explainWn8 = false) { // Yeah... it's a Player feature but it uses more calculations for tanks... if (!await CanExecute(ctx, Features.Players)) { return; } if (string.IsNullOrWhiteSpace(gamerTag)) { await ctx.RespondAsync($"Please specify the *Gamer Tag*, {ctx.User.Mention}. Something like `!w tankerTank \"{ctx.User.Username.RemoveDiacritics()}\" \"HWK 12\"`, for example."); return; } if (string.IsNullOrWhiteSpace(tankName)) { await ctx.RespondAsync($"Please specify the *Tank Name*, {ctx.User.Mention}. Something like `!w tankerTank \"{ctx.User.Username.RemoveDiacritics()}\" \"HWK 12\"`, for example."); return; } await ctx.TriggerTypingAsync(); Log.Debug($"Requesting {nameof(TankerTank)}({gamerTag}, {tankName}, {explainWn8})..."); try { var provider = new DbProvider(_connectionString); var playerCommands = new PlayerCommands(); var player = await playerCommands.GetPlayer(ctx, gamerTag); if (player == null) { Log.Debug($"Could not find player {gamerTag}"); await ctx.RespondAsync("I could not find a tanker " + $"with the Gamer Tag `{gamerTag}` on the Database, {ctx.User.Mention}. I may not track this player, or the Gamer Tag is wrong."); return; } gamerTag = player.Name; var tank = FindTank(tankName, out _); if (tank == null) { await ctx.RespondAsync($"Can't find a tank with `{tankName}` on the name, {ctx.User.Mention}."); return; } var tr = provider.GetTanksReferences(null, tank.TankId, includeMoe: false, includeHistogram: false, includeLeaders: false).FirstOrDefault(); if (tr == null) { await ctx.RespondAsync($"Sorry, there is no tank statistics for the `{tank.Name}`, {ctx.User.Mention}."); return; } var hist = provider.GetPlayerHistoryByTank(player.Platform, player.Id, tank.TankId).ToArray(); if (!hist.Any()) { await ctx.RespondAsync($"Sorry, there is no tank statistics history for the `{tank.Name}` for the player `{gamerTag}`, {ctx.User.Mention}."); return; } var wn8Expected = provider.GetWn8ExpectedValues(); foreach (var h in hist) { h.Wn8 = wn8Expected.CalculateWn8(h); } TankPlayerStatistics[] head, tail; if (hist.Length > 20) { head = hist.Take(15).ToArray(); tail = hist.Skip(hist.Length - 5).Take(5).ToArray(); } else { head = hist; tail = null; } var sb = new StringBuilder(); sb.AppendLine($"History of the {Formatter.MaskedUrl(tr.Name, new Uri(tr.Url))}, Tier {tr.Tier.ToRomanNumeral()}, {tr.Nation.GetDescription()}, {(tr.IsPremium ? "Premium" : "Regular")}, as played by {Formatter.MaskedUrl(gamerTag, new Uri(player.PlayerOverallUrl))}, {ctx.User.Mention}:"); sb.AppendLine(); sb.AppendLine("```"); sb.AppendLine("Date Battles Tot.Dmg WN8"); foreach (var t in head) { sb.AppendLine($"{t.LastBattle:yyyy-MM-dd} {t.Battles,7:N0} {t.TotalDamagePerBattle,7:N0} {t.Wn8,6:N0}"); } if (tail != null) { sb.AppendLine("..."); foreach (var t in tail) { sb.AppendLine($"{t.LastBattle:yyyy-MM-dd} {t.Battles,7:N0} {t.TotalDamagePerBattle,7:N0} {t.Wn8,6:N0}"); } } sb.AppendLine("```"); sb.AppendLine(); var ptr = player.Performance.All[tr.TankId]; sb.AppendLine($"**{player.Name}** current values:"); sb.AppendLine($"Total Damage: {ptr.TotalDamagePerBattle:N0} "); sb.AppendLine($"Direct Damage: {ptr.DirectDamagePerBattle:N0} "); sb.AppendLine($"Assisted Damage: {ptr.DamageAssistedPerBattle:N0} "); sb.AppendLine($"WN8: {ptr.Wn8:N0}; Win Rate: {ptr.WinRate:P1}"); sb.AppendLine($"Battles: {ptr.Battles:N0}; Hours Battling: {ptr.BattleLifeTime.TotalHours:N0}"); sb.AppendLine($"Max Kills: {ptr.MaxFrags:N1}; Avg Kills: {ptr.KillsPerBattle:N1}"); if (tr.LastMonth != null) { sb.AppendLine(); sb.AppendLine($"**Global Recent** average among {tr.LastMonth.TotalPlayers:N0} players of the {tr.Name}:"); sb.AppendLine($"Total Damage: {tr.LastMonth.TotalDamage:N0} "); sb.AppendLine($"Direct Damage: {tr.LastMonth.DamageDealt:N0} "); sb.AppendLine($"Assisted Damage: {tr.LastMonth.DamageAssisted:N0} "); sb.AppendLine($"WN8: {tr.LastMonth.AverageWn8:N0}; Win Rate: {tr.LastMonth.WinRatio:P1}"); sb.AppendLine($"Battles: {tr.LastMonth.BattlesPerPlayer:N0}; Hours Battling: {tr.LastMonth.TimePerPlayer.TotalHours:N0}"); sb.AppendLine($"Max Kills: {tr.LastMonth.MaxKills:N1}; Avg Kills: {tr.LastMonth.Kills:N1}"); } var color = ptr.Wn8.ToColor(); var embed = new DiscordEmbedBuilder { Title = $"{gamerTag} history with the {tank.Name}", Description = sb.ToString(), Color = new DiscordColor(color.R, color.G, color.B), ThumbnailUrl = tank.SmallImageUrl, Url = player.PlayerOverallUrl, Author = new DiscordEmbedBuilder.EmbedAuthor { Name = "WoTClans", Url = "https://wotclans.com.br" }, Footer = new DiscordEmbedBuilder.EmbedFooter { Text = $"Calculated {player.Moment.Humanize()}" } }; await ctx.RespondAsync("", embed : embed); if (!explainWn8) { return; } sb.Clear(); sb.AppendLine($"Overall WN8 Explanation for the {Formatter.MaskedUrl(tr.Name, new Uri(tr.Url))}, Tier {tr.Tier.ToRomanNumeral()}, {tr.Nation.GetDescription()}, {(tr.IsPremium ? "Premium" : "Regular")}, as played by {Formatter.MaskedUrl(gamerTag, new Uri(player.PlayerOverallUrl))}, {ctx.User.Mention}:"); sb.AppendLine(); var e = wn8Expected[tank.TankId]; sb.AppendLine("**Expected Values**"); sb.AppendLine($"eDmg = {e.Damage}"); sb.AppendLine($"eFrag = {e.Frag}"); sb.AppendLine($"eSpot = {e.Spot}"); sb.AppendLine($"eWin = {e.WinRate}"); sb.AppendLine($"eDef = {e.Def}"); sb.AppendLine(); sb.AppendLine("**Player Values**"); sb.AppendLine($"pDmg = {ptr.DirectDamagePerBattle}"); sb.AppendLine($"pFrag = {ptr.KillsPerBattle}"); sb.AppendLine($"pSpot = {ptr.SpotsPerBattle}"); sb.AppendLine($"pWin = {ptr.WinRate}"); sb.AppendLine($"pDef = {ptr.DroppedCapturePointsPerBattle}"); sb.AppendLine(); var rDmg = ptr.DirectDamagePerBattle / e.Damage; var rFrag = ptr.KillsPerBattle / e.Frag; var rSpot = ptr.SpotsPerBattle / e.Spot; var rWin = ptr.WinRate / e.WinRate; var rDef = ptr.DroppedCapturePointsPerBattle / e.Def; sb.AppendLine("**Relative Values**"); sb.AppendLine($"rDmg = pDmg/eDmg = {rDmg}"); sb.AppendLine($"rFrag = pFrag/eFrag = {rFrag}"); sb.AppendLine($"rSpot = pSpot/eSpot = {rSpot}"); sb.AppendLine($"rWin = pWin/eWin = {rWin}"); sb.AppendLine($"rDef = pDef/eDef = {rDef}"); sb.AppendLine(); var nDmg = Math.Max(0, (rDmg - 0.22) / (1.0 - 0.22)); var nWin = Math.Max(0, (rWin - 0.71) / (1.0 - 0.71)); var nFrag = Math.Max(0, Math.Min(nDmg + 0.2, (rFrag - 0.12) / (1 - 0.12))); var nSpot = Math.Max(0, Math.Min(nDmg + 0.1, (rSpot - 0.38) / (1 - 0.38))); var nDef = Math.Max(0, Math.Min(nDmg + 0.1, (rDef - 0.10) / (1 - 0.10))); sb.AppendLine("**Normalized Values**"); sb.AppendLine($"nDmg = Max(0, (rDmg - 0.22) / (1.0 - 0.22)) = {nDmg}"); sb.AppendLine($"nFrag = Max(0, Min(nDmg + 0.2, (rFrag - 0.12) / (1 - 0.12))) = {nFrag}"); sb.AppendLine($"nSpot = Max(0, Min(nDmg + 0.1, (rSpot - 0.38) / (1 - 0.38))) = {nSpot}"); sb.AppendLine($"nWin = Max(0, (rWin - 0.71) / (1.0 - 0.71)) = {nWin}"); sb.AppendLine($"nDef = Max(0, Min(nDmg + 0.1, (rDef - 0.10) / (1 - 0.10))) = {nDef}"); sb.AppendLine(); var dmgPart = 980.0 * nDmg; var winPart = 145.0 * Math.Min(1.8, nWin); var fragPart = 210.0 * nDmg * nFrag; var spotPart = 155.0 * nFrag * nSpot; var defPart = 75.0 * nFrag * nDef; sb.AppendLine("**WN8 Parts**"); sb.AppendLine($"dmgPart = 980 * nDmg = {dmgPart}"); sb.AppendLine($"fragPart = 210 * nDmg * nFrag = {fragPart}"); sb.AppendLine($"spotPart = 155 * nFrag * nSpot = {spotPart}"); sb.AppendLine($"winPart = 145 * Min(1.8, nWin) = {winPart}"); sb.AppendLine($"defPart = 75 * nFrag * nDef = {defPart}"); sb.AppendLine(); var wn8 = dmgPart + winPart + fragPart + spotPart + defPart; sb.AppendLine($"**WN8** = dmgPart + fragPart + spotPart + winPart + defPart = **{wn8}**"); embed = new DiscordEmbedBuilder { Title = $"{gamerTag} WN8 with the {tank.Name}", Description = sb.ToString(), Color = new DiscordColor(color.R, color.G, color.B), ThumbnailUrl = tank.SmallImageUrl, Url = player.PlayerOverallUrl, Author = new DiscordEmbedBuilder.EmbedAuthor { Name = "WoTClans", Url = "https://wotclans.com.br" }, Footer = new DiscordEmbedBuilder.EmbedFooter { Text = $"Calculated {player.Moment.Humanize()}" } }; await ctx.RespondAsync("", embed : embed); } catch (Exception ex) { Log.Error($"{nameof(TankerTank)}({gamerTag})", ex); await ctx.RespondAsync($"Sorry, {ctx.User.Mention}. There was an error... the *Coder* will be notified of `{ex.Message}`."); } }