From 2507a8cc7d7d88f291f64ee14bae14d5a2df0a9e Mon Sep 17 00:00:00 2001 From: barelyprofessional <150058423+barelyprofessional@users.noreply.github.com> Date: Fri, 17 Oct 2025 17:52:01 -0500 Subject: [PATCH] Re-organized the kasino commands into their own folder and split the games up into individual files --- .../Commands/Kasino/GuessWhatNumberCommand.cs | 75 +++++ .../{ => Kasino}/KasinoAdminCommands.cs | 2 +- .../{ => Kasino}/KasinoUserCommands.cs | 2 +- .../Commands/Kasino/KenoCommand.cs | 201 ++++++++++++++ .../PlanesCommand.cs} | 259 +----------------- 5 files changed, 279 insertions(+), 260 deletions(-) create mode 100644 KfChatDotNetBot/Commands/Kasino/GuessWhatNumberCommand.cs rename KfChatDotNetBot/Commands/{ => Kasino}/KasinoAdminCommands.cs (98%) rename KfChatDotNetBot/Commands/{ => Kasino}/KasinoUserCommands.cs (99%) create mode 100644 KfChatDotNetBot/Commands/Kasino/KenoCommand.cs rename KfChatDotNetBot/Commands/{KasinoGambaCommands.cs => Kasino/PlanesCommand.cs} (60%) diff --git a/KfChatDotNetBot/Commands/Kasino/GuessWhatNumberCommand.cs b/KfChatDotNetBot/Commands/Kasino/GuessWhatNumberCommand.cs new file mode 100644 index 0000000..13a6e3d --- /dev/null +++ b/KfChatDotNetBot/Commands/Kasino/GuessWhatNumberCommand.cs @@ -0,0 +1,75 @@ +using System.Text.RegularExpressions; +using KfChatDotNetBot.Extensions; +using KfChatDotNetBot.Models; +using KfChatDotNetBot.Models.DbModels; +using KfChatDotNetBot.Services; +using KfChatDotNetBot.Settings; +using KfChatDotNetWsClient.Models.Events; + +namespace KfChatDotNetBot.Commands.Kasino; + +[KasinoCommand] +[WagerCommand] +public class GuessWhatNumberCommand : ICommand +{ + public List Patterns => [ + new Regex(@"^guess (?\d+) (?\d+)$", RegexOptions.IgnoreCase), + new Regex(@"^guess (?\d+\.\d+) (?\d+)$", RegexOptions.IgnoreCase), + new Regex("^guess$") + ]; + public string? HelpText => "What number am I thinking of?"; + public UserRight RequiredRight => UserRight.Loser; + public TimeSpan Timeout => TimeSpan.FromSeconds(10); + public RateLimitOptionsModel? RateLimitOptions => null; + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromMilliseconds((await SettingsProvider.GetValueAsync(BuiltIn.Keys.KasinoGuessWhatNumberCleanupDelay)).ToType()); + if (!arguments.TryGetValue("amount", out var amount)) + { + await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, not enough arguments. !guess ", true, autoDeleteAfter: cleanupDelay); + return; + } + + var wager = Convert.ToDecimal(amount.Value); + var guess = Convert.ToInt32(arguments["number"].Value); + if (guess is < 1 or > 10) + { + await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, your guess must be between 1 and 10", true, autoDeleteAfter: cleanupDelay); + return; + } + var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); + if (gambler == null) + throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}"); + if (gambler.Balance < wager) + { + await botInstance.SendChatMessageAsync( + $"{user.FormatUsername()}, your balance of {await gambler.Balance.FormatKasinoCurrencyAsync()} isn't enough for this wager.", + true, autoDeleteAfter: cleanupDelay); + return; + } + + var answer = Money.GetRandomNumber(gambler, 1, 10); + decimal newBalance; + var colors = + await SettingsProvider.GetMultipleValuesAsync([ + BuiltIn.Keys.KiwiFarmsGreenColor, BuiltIn.Keys.KiwiFarmsRedColor + ]); + if (guess == answer) + { + var effect = wager * 9; + await Money.NewWagerAsync(gambler.Id, wager, effect, WagerGame.GuessWhatNumber, ct: ctx); + newBalance = gambler.Balance + effect; + await botInstance.SendChatMessageAsync( + $"{user.FormatUsername()}, [color={colors[BuiltIn.Keys.KiwiFarmsGreenColor].Value}]correct![/color] You won {await effect.FormatKasinoCurrencyAsync()} and your balance is now {await newBalance.FormatKasinoCurrencyAsync()}", + true, autoDeleteAfter: cleanupDelay); + return; + } + + await Money.NewWagerAsync(gambler.Id, wager, -wager, WagerGame.GuessWhatNumber, ct: ctx); + newBalance = gambler.Balance - wager; + await botInstance.SendChatMessageAsync( + $"{user.FormatUsername()}, [color={colors[BuiltIn.Keys.KiwiFarmsRedColor].Value}]wrong![/color] I was thinking of {answer}. Your balance is now {await newBalance.FormatKasinoCurrencyAsync()}", + true, autoDeleteAfter: cleanupDelay); + } +} \ No newline at end of file diff --git a/KfChatDotNetBot/Commands/KasinoAdminCommands.cs b/KfChatDotNetBot/Commands/Kasino/KasinoAdminCommands.cs similarity index 98% rename from KfChatDotNetBot/Commands/KasinoAdminCommands.cs rename to KfChatDotNetBot/Commands/Kasino/KasinoAdminCommands.cs index 5fd98e5..c7a2aca 100644 --- a/KfChatDotNetBot/Commands/KasinoAdminCommands.cs +++ b/KfChatDotNetBot/Commands/Kasino/KasinoAdminCommands.cs @@ -7,7 +7,7 @@ using KfChatDotNetBot.Services; using KfChatDotNetWsClient.Models.Events; using Microsoft.EntityFrameworkCore; -namespace KfChatDotNetBot.Commands; +namespace KfChatDotNetBot.Commands.Kasino; public class TempExcludeCommand : ICommand { diff --git a/KfChatDotNetBot/Commands/KasinoUserCommands.cs b/KfChatDotNetBot/Commands/Kasino/KasinoUserCommands.cs similarity index 99% rename from KfChatDotNetBot/Commands/KasinoUserCommands.cs rename to KfChatDotNetBot/Commands/Kasino/KasinoUserCommands.cs index 2e65066..80b8ab0 100644 --- a/KfChatDotNetBot/Commands/KasinoUserCommands.cs +++ b/KfChatDotNetBot/Commands/Kasino/KasinoUserCommands.cs @@ -10,7 +10,7 @@ using KfChatDotNetWsClient.Models.Events; using Microsoft.EntityFrameworkCore; using NLog; -namespace KfChatDotNetBot.Commands; +namespace KfChatDotNetBot.Commands.Kasino; [KasinoCommand] public class GetBalanceCommand : ICommand diff --git a/KfChatDotNetBot/Commands/Kasino/KenoCommand.cs b/KfChatDotNetBot/Commands/Kasino/KenoCommand.cs new file mode 100644 index 0000000..928161b --- /dev/null +++ b/KfChatDotNetBot/Commands/Kasino/KenoCommand.cs @@ -0,0 +1,201 @@ +using System.Text.RegularExpressions; +using KfChatDotNetBot.Extensions; +using KfChatDotNetBot.Models; +using KfChatDotNetBot.Models.DbModels; +using KfChatDotNetBot.Services; +using KfChatDotNetBot.Settings; +using KfChatDotNetWsClient.Models.Events; +using NLog; + +namespace KfChatDotNetBot.Commands.Kasino; + +[KasinoCommand] +[WagerCommand] +public class KenoCommand : ICommand +{ + public List Patterns => [ + new Regex(@"^keno (?\d+) (?\d+)$", RegexOptions.IgnoreCase), + new Regex(@"^keno (?\d+\.\d+) (?\d+)$", RegexOptions.IgnoreCase), + new Regex(@"^keno (?\d+)$", RegexOptions.IgnoreCase), + new Regex(@"^keno (?\d+\.\d+)$", RegexOptions.IgnoreCase), + new Regex("^keno$") + ]; + public string? HelpText => "!keno [bet amount] [numbers to pick(optional, default 10)]"; + public UserRight RequiredRight => UserRight.Loser; + public TimeSpan Timeout => TimeSpan.FromSeconds(60); + public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel + { + MaxInvocations = 3, + Window = TimeSpan.FromSeconds(10) + }; + + private const string PlayerNumberDisplay = "⬜"; + private const string CasinoNumberDisplay = "🔶"; + private const string MatchRevealDisplay = "💠"; + private const string BlankSpaceDisplay = "⬛"; + + private SentMessageTrackerModel _kenoTable; + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + var cleanupDelay = TimeSpan.FromMilliseconds((await SettingsProvider.GetValueAsync(BuiltIn.Keys.KasinoKenoCleanupDelay)).ToType()); + if (!arguments.TryGetValue("amount", out var amount)) //if user just enters !keno + { + await botInstance.SendChatMessageAsync( + $"{user.FormatUsername()}, not enough arguments. !keno , or !keno and 10 will be selected automatically", + true, autoDeleteAfter: cleanupDelay); + return; + } + var wager = Convert.ToDecimal(amount.Value); + var numbers = !arguments.TryGetValue("numbers", out var userNumbers) ? 10 : Convert.ToInt32(userNumbers.Value); //if user just enters !keno + var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); + if (gambler == null) + throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}"); + if (gambler.Balance < wager) + { + await botInstance.SendChatMessageAsync( + $"{user.FormatUsername()}, your balance of {await gambler.Balance.FormatKasinoCurrencyAsync()} isn't enough for this wager.", + true, autoDeleteAfter: cleanupDelay); + return; + } + + if (numbers is < 1 or > 10) //if user picks invalid numbers + { + await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, you can only pick numbers from 1 - 10", + true, autoDeleteAfter: cleanupDelay); + return; + } + + var payoutMultipliers = new[,]//stole the payout multis from stake keno and re added the RTP, except for the 1000x + { + {0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, // 1 selection + {0.0, 0.0, 17.27, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, // 2 selections + {0.0, 0.0, 0.0, 82.32, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, // 3 selections + {0.0, 0.0, 0.0, 10.1, 261.61, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, // 4 selections + {0.0, 0.0, 0.0, 4.5, 48.48, 454.54, 0.0, 0.0, 0.0, 0.0, 0.0}, // 5 selections + {0.0, 0.0, 0.0, 0.0, 11.11, 353.53, 717.17, 0.0, 0.0, 0.0, 0.0}, // 6 selections + {0.0, 0.0, 0.0, 0.0, 7.07, 90.90, 404.04, 808.08, 0.0, 0.0, 0.0}, // 7 selections + {0.0, 0.0, 0.0, 0.0, 5.05, 20.20, 272.72, 606.06, 909.09, 0.0, 0.0}, // 8 selections + {0.0, 0.0, 0.0, 0.0, 4.04, 11.11, 56.56, 505.05, 808.08, 1000.0, 0.0}, // 9 selections + {0.0, 0.0, 0.0, 0.0, 3.53, 8.08, 13.13, 63.63, 505.05, 808.08, 1000.0} // 10 selections + }; + var playerNumbers = GenerateKenoNumbers(numbers, gambler); + var casinoNumbers = GenerateKenoNumbers(10, gambler); + var matches = playerNumbers.Intersect(casinoNumbers).ToList(); + var payoutMulti = payoutMultipliers[numbers - 1, matches.Count]; + + await AnimatedDisplayTable(playerNumbers, casinoNumbers, matches, botInstance); + var colors = + await SettingsProvider.GetMultipleValuesAsync([ + BuiltIn.Keys.KiwiFarmsGreenColor, BuiltIn.Keys.KiwiFarmsRedColor + ]); + var newBalance = gambler.Balance - wager; + if (payoutMulti == 0) //you lose + { + await Money.NewWagerAsync(gambler.Id, wager, -wager, WagerGame.Keno, ct: ctx); + await botInstance.SendChatMessageAsync( + $"{user.FormatUsername()}, you [color={colors[BuiltIn.Keys.KiwiFarmsRedColor].Value}]lost {await wager.FormatKasinoCurrencyAsync()}[/color]. Your balance is now: {await newBalance.FormatKasinoCurrencyAsync()}.", + true, autoDeleteAfter: cleanupDelay); + botInstance.ScheduleMessageAutoDelete(_kenoTable, cleanupDelay); + return; + } + + //you win + var win = wager * (decimal)payoutMulti; + // Required to avoid compiler errors when trying to format it in the win message + newBalance = gambler.Balance + win; + await Money.NewWagerAsync(gambler.Id, wager, wager * (decimal)payoutMulti, WagerGame.Keno, ct: ctx); + await botInstance.SendChatMessageAsync( + $"{user.FormatUsername()}, you [color={colors[BuiltIn.Keys.KiwiFarmsGreenColor].Value}]won {await win.FormatKasinoCurrencyAsync()} with a {payoutMulti}x multi![/color]. Your balance is now: {await newBalance.FormatKasinoCurrencyAsync()}.", + true, autoDeleteAfter: cleanupDelay); + botInstance.ScheduleMessageAutoDelete(_kenoTable, cleanupDelay); + } + + private async Task AnimatedDisplayTable(List playerNumbers, List casinoNumbers, List matches, ChatBot botInstance) + { + var cleanupDelay = TimeSpan.FromMilliseconds((await SettingsProvider.GetValueAsync(BuiltIn.Keys.KasinoKenoCleanupDelay)).ToType()); + var logger = LogManager.GetCurrentClassLogger(); + var displayMessage = ""; + //keno board is 8 x 5, numbers left to right, top to bottom + //FIRST FRAME 11111111111111111111111111111 + var totalCounter = 1; + for (var column = 0; column < 5; column++) + { + for (var row = 0; row < 8; row++) + { + if (playerNumbers.Contains(totalCounter)) displayMessage += PlayerNumberDisplay; + else displayMessage += BlankSpaceDisplay; + totalCounter++; + } + displayMessage += "[br]"; + } + + _kenoTable = await botInstance.SendChatMessageAsync(displayMessage, true); + var i = 0; + while (_kenoTable.ChatMessageId == null) + { + i++; + if (_kenoTable.Status is SentMessageTrackerStatus.NotSending or SentMessageTrackerStatus.Lost) return; + if (i > 60) return; + await Task.Delay(100); + } + + var frameDelay = (await SettingsProvider.GetValueAsync(BuiltIn.Keys.KasinoKenoFrameDelay)).ToType(); + //FIRST FRAME 11111111111111111111111111111 + for (var frame = 0; frame < 10; frame++) //1 frame per casino number + { + displayMessage = ""; + totalCounter = 1; + for (var column = 0; column < 5; column++) + { + for (var row = 0; row < 8; row++) + { + if (casinoNumbers.Take(frame+1).Contains(totalCounter)) + { + + if (matches.Contains(totalCounter)) + { + displayMessage += MatchRevealDisplay; + } + else + { + displayMessage += CasinoNumberDisplay; + } + } + else if (playerNumbers.Contains(totalCounter)) displayMessage += PlayerNumberDisplay; + else displayMessage += BlankSpaceDisplay; + + totalCounter++; + } + displayMessage += "[br]"; + } + await botInstance.KfClient.EditMessageAsync(_kenoTable.ChatMessageId!.Value, displayMessage); + await Task.Delay(frameDelay); + if (displayMessage.Length <= 79 && displayMessage.Contains(BlankSpaceDisplay) && + (displayMessage.Contains(CasinoNumberDisplay) || displayMessage.Contains(MatchRevealDisplay) || + frame == 9)) continue; //every board should have blank spaces and casino numbers or matches. player numbers might be hidden by matches + logger.Error($"Casino numbers: {string.Join(",", casinoNumbers)} | Player Numbers: {string.Join(",", playerNumbers)} | Matches: {string.Join(",", matches)} | Frame: {frame - 1} | Display Board:"); + logger.Error(displayMessage); + await botInstance.SendChatMessageAsync($"Keno is bugged dewd, died on frame {frame} :bossman:", true, autoDeleteAfter: cleanupDelay); + } + } + + private List GenerateKenoNumbers(int size, GamblerDbModel gambler) + { + var numbers = new List(); + for (var i = 0; i < size; i++) + { + var repeatNum = true; + while (repeatNum) + { + var randomNum = Money.GetRandomNumber(gambler, 1, 40); + if (numbers.Contains(randomNum)) continue; + numbers.Add(randomNum); + repeatNum = false; + } + } + + return numbers; + } +} \ No newline at end of file diff --git a/KfChatDotNetBot/Commands/KasinoGambaCommands.cs b/KfChatDotNetBot/Commands/Kasino/PlanesCommand.cs similarity index 60% rename from KfChatDotNetBot/Commands/KasinoGambaCommands.cs rename to KfChatDotNetBot/Commands/Kasino/PlanesCommand.cs index 80f3993..2ed735f 100644 --- a/KfChatDotNetBot/Commands/KasinoGambaCommands.cs +++ b/KfChatDotNetBot/Commands/Kasino/PlanesCommand.cs @@ -9,264 +9,7 @@ using NLog; using RandN; using RandN.Compat; -namespace KfChatDotNetBot.Commands; - -[KasinoCommand] -[WagerCommand] -public class GuessWhatNumberCommand : ICommand -{ - public List Patterns => [ - new Regex(@"^guess (?\d+) (?\d+)$", RegexOptions.IgnoreCase), - new Regex(@"^guess (?\d+\.\d+) (?\d+)$", RegexOptions.IgnoreCase), - new Regex("^guess$") - ]; - public string? HelpText => "What number am I thinking of?"; - public UserRight RequiredRight => UserRight.Loser; - public TimeSpan Timeout => TimeSpan.FromSeconds(10); - public RateLimitOptionsModel? RateLimitOptions => null; - public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, - CancellationToken ctx) - { - var cleanupDelay = TimeSpan.FromMilliseconds((await SettingsProvider.GetValueAsync(BuiltIn.Keys.KasinoGuessWhatNumberCleanupDelay)).ToType()); - if (!arguments.TryGetValue("amount", out var amount)) - { - await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, not enough arguments. !guess ", true, autoDeleteAfter: cleanupDelay); - return; - } - - var wager = Convert.ToDecimal(amount.Value); - var guess = Convert.ToInt32(arguments["number"].Value); - if (guess is < 1 or > 10) - { - await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, your guess must be between 1 and 10", true, autoDeleteAfter: cleanupDelay); - return; - } - var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); - if (gambler == null) - throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}"); - if (gambler.Balance < wager) - { - await botInstance.SendChatMessageAsync( - $"{user.FormatUsername()}, your balance of {await gambler.Balance.FormatKasinoCurrencyAsync()} isn't enough for this wager.", - true, autoDeleteAfter: cleanupDelay); - return; - } - - var answer = Money.GetRandomNumber(gambler, 1, 10); - decimal newBalance; - var colors = - await SettingsProvider.GetMultipleValuesAsync([ - BuiltIn.Keys.KiwiFarmsGreenColor, BuiltIn.Keys.KiwiFarmsRedColor - ]); - if (guess == answer) - { - var effect = wager * 9; - await Money.NewWagerAsync(gambler.Id, wager, effect, WagerGame.GuessWhatNumber, ct: ctx); - newBalance = gambler.Balance + effect; - await botInstance.SendChatMessageAsync( - $"{user.FormatUsername()}, [color={colors[BuiltIn.Keys.KiwiFarmsGreenColor].Value}]correct![/color] You won {await effect.FormatKasinoCurrencyAsync()} and your balance is now {await newBalance.FormatKasinoCurrencyAsync()}", - true, autoDeleteAfter: cleanupDelay); - return; - } - - await Money.NewWagerAsync(gambler.Id, wager, -wager, WagerGame.GuessWhatNumber, ct: ctx); - newBalance = gambler.Balance - wager; - await botInstance.SendChatMessageAsync( - $"{user.FormatUsername()}, [color={colors[BuiltIn.Keys.KiwiFarmsRedColor].Value}]wrong![/color] I was thinking of {answer}. Your balance is now {await newBalance.FormatKasinoCurrencyAsync()}", - true, autoDeleteAfter: cleanupDelay); - } -} - -[KasinoCommand] -[WagerCommand] -public class KenoCommand : ICommand -{ - public List Patterns => [ - new Regex(@"^keno (?\d+) (?\d+)$", RegexOptions.IgnoreCase), - new Regex(@"^keno (?\d+\.\d+) (?\d+)$", RegexOptions.IgnoreCase), - new Regex(@"^keno (?\d+)$", RegexOptions.IgnoreCase), - new Regex(@"^keno (?\d+\.\d+)$", RegexOptions.IgnoreCase), - new Regex("^keno$") - ]; - public string? HelpText => "!keno [bet amount] [numbers to pick(optional, default 10)]"; - public UserRight RequiredRight => UserRight.Loser; - public TimeSpan Timeout => TimeSpan.FromSeconds(60); - public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel - { - MaxInvocations = 3, - Window = TimeSpan.FromSeconds(10) - }; - - private const string PlayerNumberDisplay = "⬜"; - private const string CasinoNumberDisplay = "🔶"; - private const string MatchRevealDisplay = "💠"; - private const string BlankSpaceDisplay = "⬛"; - - private SentMessageTrackerModel _kenoTable; - - public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, - CancellationToken ctx) - { - var cleanupDelay = TimeSpan.FromMilliseconds((await SettingsProvider.GetValueAsync(BuiltIn.Keys.KasinoKenoCleanupDelay)).ToType()); - if (!arguments.TryGetValue("amount", out var amount)) //if user just enters !keno - { - await botInstance.SendChatMessageAsync( - $"{user.FormatUsername()}, not enough arguments. !keno , or !keno and 10 will be selected automatically", - true, autoDeleteAfter: cleanupDelay); - return; - } - var wager = Convert.ToDecimal(amount.Value); - var numbers = !arguments.TryGetValue("numbers", out var userNumbers) ? 10 : Convert.ToInt32(userNumbers.Value); //if user just enters !keno - var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); - if (gambler == null) - throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}"); - if (gambler.Balance < wager) - { - await botInstance.SendChatMessageAsync( - $"{user.FormatUsername()}, your balance of {await gambler.Balance.FormatKasinoCurrencyAsync()} isn't enough for this wager.", - true, autoDeleteAfter: cleanupDelay); - return; - } - - if (numbers is < 1 or > 10) //if user picks invalid numbers - { - await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, you can only pick numbers from 1 - 10", - true, autoDeleteAfter: cleanupDelay); - return; - } - - var payoutMultipliers = new[,]//stole the payout multis from stake keno and re added the RTP, except for the 1000x - { - {0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, // 1 selection - {0.0, 0.0, 17.27, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, // 2 selections - {0.0, 0.0, 0.0, 82.32, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, // 3 selections - {0.0, 0.0, 0.0, 10.1, 261.61, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, // 4 selections - {0.0, 0.0, 0.0, 4.5, 48.48, 454.54, 0.0, 0.0, 0.0, 0.0, 0.0}, // 5 selections - {0.0, 0.0, 0.0, 0.0, 11.11, 353.53, 717.17, 0.0, 0.0, 0.0, 0.0}, // 6 selections - {0.0, 0.0, 0.0, 0.0, 7.07, 90.90, 404.04, 808.08, 0.0, 0.0, 0.0}, // 7 selections - {0.0, 0.0, 0.0, 0.0, 5.05, 20.20, 272.72, 606.06, 909.09, 0.0, 0.0}, // 8 selections - {0.0, 0.0, 0.0, 0.0, 4.04, 11.11, 56.56, 505.05, 808.08, 1000.0, 0.0}, // 9 selections - {0.0, 0.0, 0.0, 0.0, 3.53, 8.08, 13.13, 63.63, 505.05, 808.08, 1000.0} // 10 selections - }; - var playerNumbers = GenerateKenoNumbers(numbers, gambler); - var casinoNumbers = GenerateKenoNumbers(10, gambler); - var matches = playerNumbers.Intersect(casinoNumbers).ToList(); - var payoutMulti = payoutMultipliers[numbers - 1, matches.Count]; - - await AnimatedDisplayTable(playerNumbers, casinoNumbers, matches, botInstance); - var colors = - await SettingsProvider.GetMultipleValuesAsync([ - BuiltIn.Keys.KiwiFarmsGreenColor, BuiltIn.Keys.KiwiFarmsRedColor - ]); - var newBalance = gambler.Balance - wager; - if (payoutMulti == 0) //you lose - { - await Money.NewWagerAsync(gambler.Id, wager, -wager, WagerGame.Keno, ct: ctx); - await botInstance.SendChatMessageAsync( - $"{user.FormatUsername()}, you [color={colors[BuiltIn.Keys.KiwiFarmsRedColor].Value}]lost {await wager.FormatKasinoCurrencyAsync()}[/color]. Your balance is now: {await newBalance.FormatKasinoCurrencyAsync()}.", - true, autoDeleteAfter: cleanupDelay); - botInstance.ScheduleMessageAutoDelete(_kenoTable, cleanupDelay); - return; - } - - //you win - var win = wager * (decimal)payoutMulti; - // Required to avoid compiler errors when trying to format it in the win message - newBalance = gambler.Balance + win; - await Money.NewWagerAsync(gambler.Id, wager, wager * (decimal)payoutMulti, WagerGame.Keno, ct: ctx); - await botInstance.SendChatMessageAsync( - $"{user.FormatUsername()}, you [color={colors[BuiltIn.Keys.KiwiFarmsGreenColor].Value}]won {await win.FormatKasinoCurrencyAsync()} with a {payoutMulti}x multi![/color]. Your balance is now: {await newBalance.FormatKasinoCurrencyAsync()}.", - true, autoDeleteAfter: cleanupDelay); - botInstance.ScheduleMessageAutoDelete(_kenoTable, cleanupDelay); - } - - private async Task AnimatedDisplayTable(List playerNumbers, List casinoNumbers, List matches, ChatBot botInstance) - { - var cleanupDelay = TimeSpan.FromMilliseconds((await SettingsProvider.GetValueAsync(BuiltIn.Keys.KasinoKenoCleanupDelay)).ToType()); - var logger = LogManager.GetCurrentClassLogger(); - var displayMessage = ""; - //keno board is 8 x 5, numbers left to right, top to bottom - //FIRST FRAME 11111111111111111111111111111 - var totalCounter = 1; - for (var column = 0; column < 5; column++) - { - for (var row = 0; row < 8; row++) - { - if (playerNumbers.Contains(totalCounter)) displayMessage += PlayerNumberDisplay; - else displayMessage += BlankSpaceDisplay; - totalCounter++; - } - displayMessage += "[br]"; - } - - _kenoTable = await botInstance.SendChatMessageAsync(displayMessage, true); - var i = 0; - while (_kenoTable.ChatMessageId == null) - { - i++; - if (_kenoTable.Status is SentMessageTrackerStatus.NotSending or SentMessageTrackerStatus.Lost) return; - if (i > 60) return; - await Task.Delay(100); - } - - var frameDelay = (await SettingsProvider.GetValueAsync(BuiltIn.Keys.KasinoKenoFrameDelay)).ToType(); - //FIRST FRAME 11111111111111111111111111111 - for (var frame = 0; frame < 10; frame++) //1 frame per casino number - { - displayMessage = ""; - totalCounter = 1; - for (var column = 0; column < 5; column++) - { - for (var row = 0; row < 8; row++) - { - if (casinoNumbers.Take(frame+1).Contains(totalCounter)) - { - - if (matches.Contains(totalCounter)) - { - displayMessage += MatchRevealDisplay; - } - else - { - displayMessage += CasinoNumberDisplay; - } - } - else if (playerNumbers.Contains(totalCounter)) displayMessage += PlayerNumberDisplay; - else displayMessage += BlankSpaceDisplay; - - totalCounter++; - } - displayMessage += "[br]"; - } - await botInstance.KfClient.EditMessageAsync(_kenoTable.ChatMessageId!.Value, displayMessage); - await Task.Delay(frameDelay); - if (displayMessage.Length <= 79 && displayMessage.Contains(BlankSpaceDisplay) && - (displayMessage.Contains(CasinoNumberDisplay) || displayMessage.Contains(MatchRevealDisplay) || - frame == 9)) continue; //every board should have blank spaces and casino numbers or matches. player numbers might be hidden by matches - logger.Error($"Casino numbers: {string.Join(",", casinoNumbers)} | Player Numbers: {string.Join(",", playerNumbers)} | Matches: {string.Join(",", matches)} | Frame: {frame - 1} | Display Board:"); - logger.Error(displayMessage); - await botInstance.SendChatMessageAsync($"Keno is bugged dewd, died on frame {frame} :bossman:", true, autoDeleteAfter: cleanupDelay); - } - } - - private List GenerateKenoNumbers(int size, GamblerDbModel gambler) - { - var numbers = new List(); - for (var i = 0; i < size; i++) - { - var repeatNum = true; - while (repeatNum) - { - var randomNum = Money.GetRandomNumber(gambler, 1, 40); - if (numbers.Contains(randomNum)) continue; - numbers.Add(randomNum); - repeatNum = false; - } - } - - return numbers; - } -} +namespace KfChatDotNetBot.Commands.Kasino; [KasinoCommand] [WagerCommand]