Removed module projects because it can't work like that atm. Commented out package commands.
This commit is contained in:
		
							
								
								
									
										172
									
								
								NadekoBot.Core/Modules/Gambling/AnimalRacingCommands.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								NadekoBot.Core/Modules/Gambling/AnimalRacingCommands.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,172 @@ | ||||
| using Discord; | ||||
| using Discord.Commands; | ||||
| using Discord.WebSocket; | ||||
| using NadekoBot.Extensions; | ||||
| using NadekoBot.Core.Services; | ||||
| using System; | ||||
| using System.Linq; | ||||
| using System.Threading.Tasks; | ||||
| using NadekoBot.Common.Attributes; | ||||
| using NadekoBot.Modules.Gambling.Common.AnimalRacing.Exceptions; | ||||
| using NadekoBot.Modules.Gambling.Common.AnimalRacing; | ||||
| using NadekoBot.Modules.Gambling.Services; | ||||
|  | ||||
| namespace NadekoBot.Modules.Gambling | ||||
| { | ||||
|     public partial class Gambling | ||||
|     { | ||||
|         [Group] | ||||
|         public class AnimalRacingCommands : NadekoSubmodule<AnimalRaceService> | ||||
|         { | ||||
|             private readonly IBotConfigProvider _bc; | ||||
|             private readonly CurrencyService _cs; | ||||
|             private readonly DiscordSocketClient _client; | ||||
|  | ||||
|             public AnimalRacingCommands(IBotConfigProvider bc, CurrencyService cs, DiscordSocketClient client) | ||||
|             { | ||||
|                 _bc = bc; | ||||
|                 _cs = cs; | ||||
|                 _client = client; | ||||
|             } | ||||
|  | ||||
|             private IUserMessage raceMessage = null; | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             public Task Race() | ||||
|             { | ||||
|                 var ar = new AnimalRace(_cs, _bc.BotConfig.RaceAnimals.Shuffle().ToArray()); | ||||
|                 if (!_service.AnimalRaces.TryAdd(Context.Guild.Id, ar)) | ||||
|                     return Context.Channel.SendErrorAsync(GetText("animal_race"), GetText("animal_race_already_started")); | ||||
|  | ||||
|                 ar.Initialize(); | ||||
|  | ||||
|                 var count = 0; | ||||
|                 Task _client_MessageReceived(SocketMessage arg) | ||||
|                 { | ||||
|                     var _ = Task.Run(() => { | ||||
|                         try | ||||
|                         { | ||||
|                             if (arg.Channel.Id == Context.Channel.Id) | ||||
|                             { | ||||
|                                 if (ar.CurrentPhase  == AnimalRace.Phase.Running && ++count % 9 == 0) | ||||
|                                 { | ||||
|                                     raceMessage = null; | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                         catch { } | ||||
|                     }); | ||||
|                     return Task.CompletedTask; | ||||
|                 } | ||||
|  | ||||
|                 Task Ar_OnEnded(AnimalRace race) | ||||
|                 { | ||||
|                     _client.MessageReceived -= _client_MessageReceived; | ||||
|                     _service.AnimalRaces.TryRemove(Context.Guild.Id, out _); | ||||
|                     var winner = race.FinishedUsers[0]; | ||||
|                     if (race.FinishedUsers[0].Bet > 0) | ||||
|                     { | ||||
|                         return Context.Channel.SendConfirmAsync(GetText("animal_race"), | ||||
|                                             GetText("animal_race_won_money", Format.Bold(winner.Username), | ||||
|                                                 winner.Animal.Icon, (race.FinishedUsers[0].Bet * (race.Users.Length - 1)) + _bc.BotConfig.CurrencySign)); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         return Context.Channel.SendConfirmAsync(GetText("animal_race"), | ||||
|                             GetText("animal_race_won", Format.Bold(winner.Username), winner.Animal.Icon)); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 ar.OnStartingFailed += Ar_OnStartingFailed; | ||||
|                 ar.OnStateUpdate += Ar_OnStateUpdate; | ||||
|                 ar.OnEnded += Ar_OnEnded; | ||||
|                 ar.OnStarted += Ar_OnStarted; | ||||
|                 _client.MessageReceived += _client_MessageReceived; | ||||
|  | ||||
|                 return Context.Channel.SendConfirmAsync(GetText("animal_race"), GetText("animal_race_starting"), | ||||
|                                     footer: GetText("animal_race_join_instr", Prefix)); | ||||
|             } | ||||
|  | ||||
|             private Task Ar_OnStarted(AnimalRace race) | ||||
|             { | ||||
|                 if(race.Users.Length == race.MaxUsers) | ||||
|                     return Context.Channel.SendConfirmAsync(GetText("animal_race"), GetText("animal_race_full")); | ||||
|                 else | ||||
|                     return Context.Channel.SendConfirmAsync(GetText("animal_race"), GetText("animal_race_starting_with_x", race.Users.Length)); | ||||
|             } | ||||
|  | ||||
|             private async Task Ar_OnStateUpdate(AnimalRace race) | ||||
|             { | ||||
|                 var text = $@"|🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🔚| | ||||
| {String.Join("\n", race.Users.Select(p => | ||||
|                 { | ||||
|                     var index = race.FinishedUsers.IndexOf(p); | ||||
|                     var extra = (index == -1 ? "" : $"#{index + 1} {(index == 0 ? "🏆" : "")}"); | ||||
|                     return $"{(int)(p.Progress / 60f * 100),-2}%|{new string('‣', p.Progress) + p.Animal.Icon + extra}"; | ||||
|                 }))} | ||||
| |🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🔚|"; | ||||
|  | ||||
|                 var msg = raceMessage; | ||||
|  | ||||
|                 if (msg == null) | ||||
|                     raceMessage = await Context.Channel.SendConfirmAsync(text) | ||||
|                         .ConfigureAwait(false); | ||||
|                 else | ||||
|                     await msg.ModifyAsync(x => x.Embed = new EmbedBuilder() | ||||
|                         .WithTitle(GetText("animal_race")) | ||||
|                         .WithDescription(text) | ||||
|                         .WithOkColor() | ||||
|                         .Build()) | ||||
|                             .ConfigureAwait(false); | ||||
|             } | ||||
|  | ||||
|             private Task Ar_OnStartingFailed(AnimalRace race) | ||||
|             { | ||||
|                 _service.AnimalRaces.TryRemove(Context.Guild.Id, out _); | ||||
|                 return ReplyErrorLocalized("animal_race_failed"); | ||||
|             } | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             public async Task JoinRace(int amount = 0) | ||||
|             { | ||||
|                 if (!_service.AnimalRaces.TryGetValue(Context.Guild.Id, out var ar)) | ||||
|                 { | ||||
|                     await ReplyErrorLocalized("race_not_exist").ConfigureAwait(false); | ||||
|                     return; | ||||
|                 } | ||||
|                 try | ||||
|                 { | ||||
|                     var user = await ar.JoinRace(Context.User.Id, Context.User.ToString(), amount) | ||||
|                         .ConfigureAwait(false); | ||||
|                     if (amount > 0) | ||||
|                         await Context.Channel.SendConfirmAsync(GetText("animal_race_join_bet", Context.User.Mention, user.Animal.Icon, amount + _bc.BotConfig.CurrencySign)).ConfigureAwait(false); | ||||
|                     else | ||||
|                         await Context.Channel.SendConfirmAsync(GetText("animal_race_join", Context.User.Mention, user.Animal.Icon)).ConfigureAwait(false); | ||||
|                 } | ||||
|                 catch (ArgumentOutOfRangeException) | ||||
|                 { | ||||
|                     //ignore if user inputed an invalid amount | ||||
|                 } | ||||
|                 catch (AlreadyJoinedException) | ||||
|                 { | ||||
|                     // just ignore this | ||||
|                 } | ||||
|                 catch (AlreadyStartedException) | ||||
|                 { | ||||
|                     //ignore | ||||
|                 } | ||||
|                 catch (AnimalRaceFullException) | ||||
|                 { | ||||
|                     await Context.Channel.SendConfirmAsync(GetText("animal_race"), GetText("animal_race_full")) | ||||
|                         .ConfigureAwait(false); | ||||
|                 } | ||||
|                 catch (NotEnoughFundsException) | ||||
|                 { | ||||
|                     await Context.Channel.SendErrorAsync(GetText("not_enough", _bc.BotConfig.CurrencySign)).ConfigureAwait(false); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,161 @@ | ||||
| using NadekoBot.Common; | ||||
| using NadekoBot.Extensions; | ||||
| using NadekoBot.Modules.Gambling.Common.AnimalRacing.Exceptions; | ||||
| using NadekoBot.Core.Services; | ||||
| using NadekoBot.Core.Services.Database.Models; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | ||||
| using System.Linq; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Modules.Gambling.Common.AnimalRacing | ||||
| { | ||||
|     public class AnimalRace : IDisposable | ||||
|     { | ||||
|         public enum Phase | ||||
|         { | ||||
|             WaitingForPlayers, | ||||
|             Running, | ||||
|             Ended, | ||||
|         } | ||||
|  | ||||
|         private const int _startingDelayMiliseconds = 20_000; | ||||
|  | ||||
|         public Phase CurrentPhase = Phase.WaitingForPlayers; | ||||
|  | ||||
|         public event Func<AnimalRace, Task> OnStarted = delegate { return Task.CompletedTask; }; | ||||
|         public event Func<AnimalRace, Task> OnStartingFailed = delegate { return Task.CompletedTask; }; | ||||
|         public event Func<AnimalRace, Task> OnStateUpdate = delegate { return Task.CompletedTask; }; | ||||
|         public event Func<AnimalRace, Task> OnEnded = delegate { return Task.CompletedTask; }; | ||||
|  | ||||
|         public ImmutableArray<AnimalRacingUser> Users => _users.ToImmutableArray(); | ||||
|         public List<AnimalRacingUser> FinishedUsers { get; } = new List<AnimalRacingUser>(); | ||||
|  | ||||
|         private readonly SemaphoreSlim _locker = new SemaphoreSlim(1, 1); | ||||
|         private readonly HashSet<AnimalRacingUser> _users = new HashSet<AnimalRacingUser>(); | ||||
|         private readonly CurrencyService _currency; | ||||
|         private readonly Queue<RaceAnimal> _animalsQueue; | ||||
|         public int MaxUsers { get; } | ||||
|  | ||||
|         public AnimalRace(CurrencyService currency, RaceAnimal[] availableAnimals) | ||||
|         { | ||||
|             this._currency = currency; | ||||
|             this._animalsQueue = new Queue<RaceAnimal>(availableAnimals); | ||||
|             this.MaxUsers = availableAnimals.Length; | ||||
|  | ||||
|             if (this._animalsQueue.Count == 0) | ||||
|                 CurrentPhase = Phase.Ended; | ||||
|         } | ||||
|  | ||||
|         public void Initialize() //lame name | ||||
|         { | ||||
|             var _t = Task.Run(async () => | ||||
|             { | ||||
|                 await Task.Delay(_startingDelayMiliseconds).ConfigureAwait(false); | ||||
|  | ||||
|                 await _locker.WaitAsync().ConfigureAwait(false); | ||||
|                 try | ||||
|                 { | ||||
|                     if (CurrentPhase != Phase.WaitingForPlayers) | ||||
|                         return; | ||||
|  | ||||
|                     await Start().ConfigureAwait(false); | ||||
|                 } | ||||
|                 finally { _locker.Release(); } | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         public async Task<AnimalRacingUser> JoinRace(ulong userId, string userName, int bet = 0) | ||||
|         { | ||||
|             if (bet < 0) | ||||
|                 throw new ArgumentOutOfRangeException(nameof(bet)); | ||||
|  | ||||
|             var user = new AnimalRacingUser(userName, userId, bet); | ||||
|  | ||||
|             await _locker.WaitAsync().ConfigureAwait(false); | ||||
|             try | ||||
|             { | ||||
|                 if (_users.Count == MaxUsers) | ||||
|                     throw new AnimalRaceFullException(); | ||||
|  | ||||
|                 if (CurrentPhase != Phase.WaitingForPlayers) | ||||
|                     throw new AlreadyStartedException(); | ||||
|  | ||||
|                 if (!await _currency.RemoveAsync(userId, "BetRace", bet).ConfigureAwait(false)) | ||||
|                     throw new NotEnoughFundsException(); | ||||
|  | ||||
|                 if (_users.Contains(user)) | ||||
|                     throw new AlreadyJoinedException(); | ||||
|  | ||||
|                 var animal = _animalsQueue.Dequeue(); | ||||
|                 user.Animal = animal; | ||||
|                 _users.Add(user); | ||||
|                  | ||||
|                 if (_animalsQueue.Count == 0) //start if no more spots left | ||||
|                     await Start().ConfigureAwait(false); | ||||
|  | ||||
|                 return user; | ||||
|             } | ||||
|             finally { _locker.Release(); } | ||||
|         } | ||||
|  | ||||
|         private async Task Start() | ||||
|         { | ||||
|             CurrentPhase = Phase.Running; | ||||
|             if (_users.Count <= 1) | ||||
|             { | ||||
|                 foreach (var user in _users) | ||||
|                 { | ||||
|                     if(user.Bet > 0) | ||||
|                         await _currency.AddAsync(user.UserId, "Race refund", user.Bet).ConfigureAwait(false); | ||||
|                 } | ||||
|                  | ||||
|                 var _sf = OnStartingFailed?.Invoke(this); | ||||
|                 CurrentPhase = Phase.Ended; | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             var _ = OnStarted?.Invoke(this); | ||||
|             var _t = Task.Run(async () => | ||||
|             { | ||||
|                 var rng = new NadekoRandom(); | ||||
|                 while (!_users.All(x => x.Progress >= 60)) | ||||
|                 { | ||||
|                     foreach (var user in _users) | ||||
|                     { | ||||
|                         user.Progress += rng.Next(1, 11); | ||||
|                         if (user.Progress >= 60) | ||||
|                             user.Progress = 60; | ||||
|                     } | ||||
|  | ||||
|                     var finished = _users.Where(x => x.Progress >= 60 && !FinishedUsers.Contains(x)) | ||||
|                         .Shuffle(); | ||||
|  | ||||
|                     FinishedUsers.AddRange(finished); | ||||
|  | ||||
|                     var _ignore = OnStateUpdate?.Invoke(this); | ||||
|                     await Task.Delay(2500).ConfigureAwait(false); | ||||
|                 } | ||||
|  | ||||
|                 if (FinishedUsers[0].Bet > 0) | ||||
|                     await _currency.AddAsync(FinishedUsers[0].UserId, "Won a Race", FinishedUsers[0].Bet * (_users.Count - 1)) | ||||
|                         .ConfigureAwait(false); | ||||
|  | ||||
|                 var _ended = OnEnded?.Invoke(this); | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         public void Dispose() | ||||
|         { | ||||
|             CurrentPhase = Phase.Ended; | ||||
|             OnStarted = null; | ||||
|             OnEnded = null; | ||||
|             OnStartingFailed = null; | ||||
|             OnStateUpdate = null; | ||||
|             _locker.Dispose(); | ||||
|             _users.Clear(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,32 @@ | ||||
| using NadekoBot.Core.Services.Database.Models; | ||||
|  | ||||
| namespace NadekoBot.Modules.Gambling.Common.AnimalRacing | ||||
| { | ||||
|     public class AnimalRacingUser | ||||
|     { | ||||
|         public int Bet { get; } | ||||
|         public string Username { get; } | ||||
|         public ulong UserId { get; } | ||||
|         public RaceAnimal Animal { get; set; } | ||||
|         public int Progress { get; set; } | ||||
|  | ||||
|         public AnimalRacingUser(string username, ulong userId, int bet) | ||||
|         { | ||||
|             this.Bet = bet; | ||||
|             this.Username = username; | ||||
|             this.UserId = userId; | ||||
|         } | ||||
|  | ||||
|         public override bool Equals(object obj) | ||||
|         { | ||||
|             return obj is AnimalRacingUser x | ||||
|                 ? x.UserId == this.UserId | ||||
|                 : false; | ||||
|         } | ||||
|  | ||||
|         public override int GetHashCode() | ||||
|         { | ||||
|             return this.UserId.GetHashCode(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,9 @@ | ||||
| using System; | ||||
|  | ||||
| namespace NadekoBot.Modules.Gambling.Common.AnimalRacing.Exceptions | ||||
| { | ||||
|     public class AlreadyJoinedException : Exception | ||||
|     { | ||||
|          | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,9 @@ | ||||
| using System; | ||||
|  | ||||
| namespace NadekoBot.Modules.Gambling.Common.AnimalRacing.Exceptions | ||||
| { | ||||
|     public class AlreadyStartedException : Exception | ||||
|     { | ||||
|          | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,8 @@ | ||||
| using System; | ||||
|  | ||||
| namespace NadekoBot.Modules.Gambling.Common.AnimalRacing.Exceptions | ||||
| { | ||||
|     public class AnimalRaceFullException : Exception | ||||
|     { | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,9 @@ | ||||
| using System; | ||||
|  | ||||
| namespace NadekoBot.Modules.Gambling.Common.AnimalRacing.Exceptions | ||||
| { | ||||
|     public class NotEnoughFundsException : Exception | ||||
|     { | ||||
|          | ||||
|     } | ||||
| } | ||||
							
								
								
									
										234
									
								
								NadekoBot.Core/Modules/Gambling/Common/Cards.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										234
									
								
								NadekoBot.Core/Modules/Gambling/Common/Cards.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,234 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using NadekoBot.Common; | ||||
| using NadekoBot.Extensions; | ||||
|  | ||||
| namespace NadekoBot.Modules.Gambling.Common | ||||
| { | ||||
|     public class Cards | ||||
|     { | ||||
|         private static readonly Dictionary<int, string> cardNames = new Dictionary<int, string>() { | ||||
|         { 1, "Ace" }, | ||||
|         { 2, "Two" }, | ||||
|         { 3, "Three" }, | ||||
|         { 4, "Four" }, | ||||
|         { 5, "Five" }, | ||||
|         { 6, "Six" }, | ||||
|         { 7, "Seven" }, | ||||
|         { 8, "Eight" }, | ||||
|         { 9, "Nine" }, | ||||
|         { 10, "Ten" }, | ||||
|         { 11, "Jack" }, | ||||
|         { 12, "Queen" }, | ||||
|         { 13, "King" } | ||||
|     }; | ||||
|         private static Dictionary<string, Func<List<Card>, bool>> handValues; | ||||
|  | ||||
|  | ||||
|         public enum CardSuit | ||||
|         { | ||||
|             Spades = 1, | ||||
|             Hearts = 2, | ||||
|             Diamonds = 3, | ||||
|             Clubs = 4 | ||||
|         } | ||||
|  | ||||
|         public class Card : IComparable | ||||
|         { | ||||
|             public CardSuit Suit { get; } | ||||
|             public int Number { get; } | ||||
|  | ||||
|             public string Name | ||||
|             { | ||||
|                 get | ||||
|                 { | ||||
|                     var str = ""; | ||||
|  | ||||
|                     if (Number <= 10 && Number > 1) | ||||
|                     { | ||||
|                         str += "_" + Number; | ||||
|                     } | ||||
|                     else { | ||||
|                         str += GetName().ToLower(); | ||||
|                     } | ||||
|                     return str + "_of_" + Suit.ToString().ToLower(); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             public Card(CardSuit s, int cardNum) | ||||
|             { | ||||
|                 this.Suit = s; | ||||
|                 this.Number = cardNum; | ||||
|             } | ||||
|  | ||||
|             public string GetName() => cardNames[Number]; | ||||
|  | ||||
|             public override string ToString() => cardNames[Number] + " Of " + Suit; | ||||
|  | ||||
|             public int CompareTo(object obj) | ||||
|             { | ||||
|                 if (!(obj is Card)) return 0; | ||||
|                 var c = (Card)obj; | ||||
|                 return this.Number - c.Number; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         private List<Card> cardPool; | ||||
|         public List<Card> CardPool | ||||
|         { | ||||
|             get { return cardPool; } | ||||
|             set { cardPool = value; } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Creates a new instance of the BlackJackGame, this allows you to create multiple games running at one time. | ||||
|         /// </summary> | ||||
|         public Cards() | ||||
|         { | ||||
|             cardPool = new List<Card>(52); | ||||
|             RefillPool(); | ||||
|             InitHandValues(); | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// Restart the game of blackjack. It will only refill the pool for now. Probably wont be used, unless you want to have only 1 bjg running at one time, | ||||
|         /// then you will restart the same game every time. | ||||
|         /// </summary> | ||||
|         public void Restart() => RefillPool(); | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Removes all cards from the pool and refills the pool with all of the possible cards. NOTE: I think this is too expensive. | ||||
|         /// We should probably make it so it copies another premade list with all the cards, or something. | ||||
|         /// </summary> | ||||
|         private void RefillPool() | ||||
|         { | ||||
|             cardPool.Clear(); | ||||
|             //foreach suit | ||||
|             for (var j = 1; j < 14; j++) | ||||
|             { | ||||
|                 // and number | ||||
|                 for (var i = 1; i < 5; i++) | ||||
|                 { | ||||
|                     //generate a card of that suit and number and add it to the pool | ||||
|  | ||||
|                     // the pool will go from ace of spades,hears,diamonds,clubs all the way to the king of spades. hearts, ... | ||||
|                     cardPool.Add(new Card((CardSuit)i, j)); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         private Random r = new NadekoRandom(); | ||||
|         /// <summary> | ||||
|         /// Take a card from the pool, you either take it from the top if the deck is shuffled, or from a random place if the deck is in the default order. | ||||
|         /// </summary> | ||||
|         /// <returns>A card from the pool</returns> | ||||
|         public Card DrawACard() | ||||
|         { | ||||
|             if (CardPool.Count == 0) | ||||
|                 Restart(); | ||||
|             //you can either do this if your deck is not shuffled | ||||
|  | ||||
|             var num = r.Next(0, cardPool.Count); | ||||
|             var c = cardPool[num]; | ||||
|             cardPool.RemoveAt(num); | ||||
|             return c; | ||||
|  | ||||
|             // if you want to shuffle when you fill, then take the first one | ||||
|             /* | ||||
|             Card c = cardPool[0]; | ||||
|             cardPool.RemoveAt(0); | ||||
|             return c; | ||||
|             */ | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// Shuffles the deck. Use this if you want to take cards from the top of the deck, instead of randomly. See DrawACard method. | ||||
|         /// </summary> | ||||
|         private void Shuffle() | ||||
|         { | ||||
|             if (cardPool.Count <= 1) return; | ||||
|             var orderedPool = cardPool.Shuffle(); | ||||
|             cardPool = cardPool as List<Card> ?? orderedPool.ToList(); | ||||
|         } | ||||
|         public override string ToString() => string.Concat(cardPool.Select(c => c.ToString())) + Environment.NewLine; | ||||
|  | ||||
|         private static void InitHandValues() | ||||
|         { | ||||
|             Func<List<Card>, bool> hasPair = | ||||
|                                   cards => cards.GroupBy(card => card.Number) | ||||
|                                                 .Count(group => group.Count() == 2) == 1; | ||||
|             Func<List<Card>, bool> isPair = | ||||
|                                   cards => cards.GroupBy(card => card.Number) | ||||
|                                                 .Count(group => group.Count() == 3) == 0 | ||||
|                                            && hasPair(cards); | ||||
|  | ||||
|             Func<List<Card>, bool> isTwoPair = | ||||
|                                   cards => cards.GroupBy(card => card.Number) | ||||
|                                                 .Count(group => group.Count() == 2) == 2; | ||||
|  | ||||
|             Func<List<Card>, bool> isStraight = | ||||
|                 cards => | ||||
|                 { | ||||
|                     if (cards.GroupBy(card => card.Number).Count() != cards.Count()) | ||||
|                         return false; | ||||
|                     var toReturn = (cards.Max(card => (int)card.Number) | ||||
|                                         - cards.Min(card => (int)card.Number) == 4); | ||||
|                     if (toReturn || cards.All(c => c.Number != 1)) return toReturn; | ||||
|  | ||||
|                     var newCards = cards.Select(c => c.Number == 1 ? new Card(c.Suit, 14) : c); | ||||
|                     return (newCards.Max(card => (int)card.Number) | ||||
|                             - newCards.Min(card => (int)card.Number) == 4); | ||||
|                 }; | ||||
|  | ||||
|             Func<List<Card>, bool> hasThreeOfKind = | ||||
|                                   cards => cards.GroupBy(card => card.Number) | ||||
|                                                 .Any(group => group.Count() == 3); | ||||
|  | ||||
|             Func<List<Card>, bool> isThreeOfKind = | ||||
|                                   cards => hasThreeOfKind(cards) && !hasPair(cards); | ||||
|  | ||||
|             Func<List<Card>, bool> isFlush = | ||||
|                                   cards => cards.GroupBy(card => card.Suit).Count() == 1; | ||||
|  | ||||
|             Func<List<Card>, bool> isFourOfKind = | ||||
|                                   cards => cards.GroupBy(card => card.Number) | ||||
|                                                 .Any(group => group.Count() == 4); | ||||
|  | ||||
|             Func<List<Card>, bool> isFullHouse = | ||||
|                                   cards => hasPair(cards) && hasThreeOfKind(cards); | ||||
|  | ||||
|             Func<List<Card>, bool> hasStraightFlush = | ||||
|                                   cards => isFlush(cards) && isStraight(cards); | ||||
|  | ||||
|             Func<List<Card>, bool> isRoyalFlush = | ||||
|                                   cards => cards.Min(card => card.Number) == 1 && | ||||
|                                            cards.Max(card => card.Number) == 13 | ||||
|                                            && hasStraightFlush(cards); | ||||
|  | ||||
|             Func<List<Card>, bool> isStraightFlush = | ||||
|                                   cards => hasStraightFlush(cards) && !isRoyalFlush(cards); | ||||
|  | ||||
|             handValues = new Dictionary<string, Func<List<Card>, bool>> | ||||
|             { | ||||
|                 { "Royal Flush", isRoyalFlush }, | ||||
|                 { "Straight Flush", isStraightFlush }, | ||||
|                 { "Four Of A Kind", isFourOfKind }, | ||||
|                 { "Full House", isFullHouse }, | ||||
|                 { "Flush", isFlush }, | ||||
|                 { "Straight", isStraight }, | ||||
|                 { "Three Of A Kind", isThreeOfKind }, | ||||
|                 { "Two Pairs", isTwoPair }, | ||||
|                 { "A Pair", isPair } | ||||
|             }; | ||||
|         } | ||||
|  | ||||
|         public static string GetHandValue(List<Card> cards) | ||||
|         { | ||||
|             if (handValues == null) | ||||
|                 InitHandValues(); | ||||
|             foreach (var kvp in handValues.Where(x => x.Value(cards))) | ||||
|             { | ||||
|                 return kvp.Key; | ||||
|             } | ||||
|             return "High card " + (cards.FirstOrDefault(c => c.Number == 1)?.GetName() ?? cards.Max().GetName()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,12 @@ | ||||
| using Discord; | ||||
| using Discord.Commands; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Modules.Gambling.Common | ||||
| { | ||||
|     public abstract class CurrencyEvent | ||||
|     { | ||||
|         public abstract Task Stop(); | ||||
|         public abstract Task Start(IUserMessage msg, ICommandContext channel); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,144 @@ | ||||
| using Discord; | ||||
| using Discord.Commands; | ||||
| using Discord.WebSocket; | ||||
| using NadekoBot.Common.Collections; | ||||
| using NadekoBot.Extensions; | ||||
| using NadekoBot.Core.Services; | ||||
| using NadekoBot.Core.Services.Database.Models; | ||||
| using NLog; | ||||
| using System; | ||||
| using System.Collections.Concurrent; | ||||
| using System.Collections.Generic; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Modules.Gambling.Common | ||||
| { | ||||
|     public class ReactionEvent : CurrencyEvent | ||||
|     { | ||||
|         private readonly ConcurrentHashSet<ulong> _reactionAwardedUsers = new ConcurrentHashSet<ulong>(); | ||||
|         private readonly BotConfig _bc; | ||||
|         private readonly Logger _log; | ||||
|         private readonly DiscordSocketClient _client; | ||||
|         private readonly CurrencyService _cs; | ||||
|         private readonly SocketSelfUser _botUser; | ||||
|  | ||||
|         private IUserMessage StartingMessage { get; set; } | ||||
|  | ||||
|         private CancellationTokenSource Source { get; } | ||||
|         private CancellationToken CancelToken { get; } | ||||
|  | ||||
|         private readonly ConcurrentQueue<ulong> _toGiveTo = new ConcurrentQueue<ulong>(); | ||||
|         private readonly int _amount; | ||||
|  | ||||
|         public ReactionEvent(BotConfig bc, DiscordSocketClient client, CurrencyService cs, int amount) | ||||
|         { | ||||
|             _bc = bc; | ||||
|             _log = LogManager.GetCurrentClassLogger(); | ||||
|             _client = client; | ||||
|             _cs = cs; | ||||
|             _botUser = client.CurrentUser; | ||||
|             _amount = amount; | ||||
|             Source = new CancellationTokenSource(); | ||||
|             CancelToken = Source.Token; | ||||
|  | ||||
|             var _ = Task.Run(async () => | ||||
|             { | ||||
|                 var users = new List<ulong>(); | ||||
|                 while (!CancelToken.IsCancellationRequested) | ||||
|                 { | ||||
|                     await Task.Delay(1000).ConfigureAwait(false); | ||||
|                     while (_toGiveTo.TryDequeue(out var usrId)) | ||||
|                     { | ||||
|                         users.Add(usrId); | ||||
|                     } | ||||
|  | ||||
|                     if (users.Count > 0) | ||||
|                     { | ||||
|                         await _cs.AddToManyAsync("Reaction Event", _amount, users.ToArray()).ConfigureAwait(false); | ||||
|                     } | ||||
|  | ||||
|                     users.Clear(); | ||||
|                 } | ||||
|             }, CancelToken); | ||||
|         } | ||||
|  | ||||
|         public override async Task Stop() | ||||
|         { | ||||
|             if (StartingMessage != null) | ||||
|                 await StartingMessage.DeleteAsync().ConfigureAwait(false); | ||||
|  | ||||
|             if (!Source.IsCancellationRequested) | ||||
|                 Source.Cancel(); | ||||
|  | ||||
|             _client.MessageDeleted -= MessageDeletedEventHandler; | ||||
|         } | ||||
|  | ||||
|         private Task MessageDeletedEventHandler(Cacheable<IMessage, ulong> msg, ISocketMessageChannel channel) | ||||
|         { | ||||
|             if (StartingMessage?.Id == msg.Id) | ||||
|             { | ||||
|                 _log.Warn("Stopping flower reaction event because message is deleted."); | ||||
|                 var __ = Task.Run(Stop); | ||||
|             } | ||||
|  | ||||
|             return Task.CompletedTask; | ||||
|         } | ||||
|  | ||||
|         public override async Task Start(IUserMessage umsg, ICommandContext context) | ||||
|         { | ||||
|             StartingMessage = umsg; | ||||
|             _client.MessageDeleted += MessageDeletedEventHandler; | ||||
|  | ||||
|             IEmote iemote; | ||||
|             if (Emote.TryParse(_bc.CurrencySign, out var emote)) | ||||
|             { | ||||
|                 iemote = emote; | ||||
|             } | ||||
|             else | ||||
|                 iemote = new Emoji(_bc.CurrencySign); | ||||
|             try { await StartingMessage.AddReactionAsync(iemote).ConfigureAwait(false); } | ||||
|             catch | ||||
|             { | ||||
|                 try { await StartingMessage.AddReactionAsync(iemote).ConfigureAwait(false); } | ||||
|                 catch | ||||
|                 { | ||||
|                     try { await StartingMessage.DeleteAsync().ConfigureAwait(false); } | ||||
|                     catch { return; } | ||||
|                 } | ||||
|             } | ||||
|             using (StartingMessage.OnReaction(_client, (r) => | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     if (r.UserId == _botUser.Id) | ||||
|                         return; | ||||
|  | ||||
|                     if (r.Emote.Name == iemote.Name && r.User.IsSpecified && ((DateTime.UtcNow - r.User.Value.CreatedAt).TotalDays > 5) && _reactionAwardedUsers.Add(r.User.Value.Id)) | ||||
|                     { | ||||
|                         _toGiveTo.Enqueue(r.UserId); | ||||
|                     } | ||||
|                 } | ||||
|                 catch | ||||
|                 { | ||||
|                     // ignored | ||||
|                 } | ||||
|             })) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     await Task.Delay(TimeSpan.FromHours(24), CancelToken).ConfigureAwait(false); | ||||
|                 } | ||||
|                 catch (OperationCanceledException) | ||||
|                 { | ||||
|  | ||||
|                 } | ||||
|                 if (CancelToken.IsCancellationRequested) | ||||
|                     return; | ||||
|  | ||||
|                 _log.Warn("Stopping flower reaction event because it expired."); | ||||
|                 await Stop(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,99 @@ | ||||
| using Discord; | ||||
| using Discord.Commands; | ||||
| using Discord.WebSocket; | ||||
| using NadekoBot.Common; | ||||
| using NadekoBot.Common.Collections; | ||||
| using NadekoBot.Core.Services; | ||||
| using System; | ||||
| using System.Linq; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Modules.Gambling.Common.CurrencyEvents | ||||
| { | ||||
|     public class SneakyEvent : CurrencyEvent | ||||
|     { | ||||
|         public event Action OnEnded; | ||||
|         public string Code { get; private set; } = string.Empty; | ||||
|         private readonly ConcurrentHashSet<ulong> _sneakyGameAwardedUsers = new ConcurrentHashSet<ulong>(); | ||||
|  | ||||
|         private readonly char[] _sneakyGameStatusChars = Enumerable.Range(48, 10) | ||||
|             .Concat(Enumerable.Range(65, 26)) | ||||
|             .Concat(Enumerable.Range(97, 26)) | ||||
|             .Select(x => (char)x) | ||||
|             .ToArray(); | ||||
|  | ||||
|         private readonly CurrencyService _cs; | ||||
|         private readonly DiscordSocketClient _client; | ||||
|         private readonly IBotConfigProvider _bc; | ||||
|         private readonly int _length; | ||||
|  | ||||
|         public SneakyEvent(CurrencyService cs, DiscordSocketClient client, | ||||
|             IBotConfigProvider bc, int len) | ||||
|         { | ||||
|             _cs = cs; | ||||
|             _client = client; | ||||
|             _bc = bc; | ||||
|             _length = len; | ||||
|         } | ||||
|  | ||||
|         public override async Task Start(IUserMessage msg, ICommandContext channel) | ||||
|         { | ||||
|             GenerateCode(); | ||||
|  | ||||
|             //start the event | ||||
|             _client.MessageReceived += SneakyGameMessageReceivedEventHandler; | ||||
|             await _client.SetGameAsync($"type {Code} for " + _bc.BotConfig.CurrencyPluralName) | ||||
|                 .ConfigureAwait(false); | ||||
|             await Task.Delay(_length * 1000).ConfigureAwait(false); | ||||
|             await Stop().ConfigureAwait(false); | ||||
|         } | ||||
|  | ||||
|         private void GenerateCode() | ||||
|         { | ||||
|             var rng = new NadekoRandom(); | ||||
|  | ||||
|             for (var i = 0; i < 5; i++) | ||||
|             { | ||||
|                 Code += _sneakyGameStatusChars[rng.Next(0, _sneakyGameStatusChars.Length)]; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public override async Task Stop() | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 _client.MessageReceived -= SneakyGameMessageReceivedEventHandler; | ||||
|                 Code = string.Empty; | ||||
|                 _sneakyGameAwardedUsers.Clear(); | ||||
|                 await _client.SetGameAsync(null).ConfigureAwait(false); | ||||
|             } | ||||
|             catch { } | ||||
|             finally | ||||
|             { | ||||
|  | ||||
|                 OnEnded?.Invoke(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         private Task SneakyGameMessageReceivedEventHandler(SocketMessage arg) | ||||
|         { | ||||
|             if (arg.Content == Code && | ||||
|                 _sneakyGameAwardedUsers.Add(arg.Author.Id)) | ||||
|             { | ||||
|                 var _ = Task.Run(async () => | ||||
|                 { | ||||
|                     await _cs.AddAsync(arg.Author, "Sneaky Game Event", 100, false) | ||||
|                         .ConfigureAwait(false); | ||||
|  | ||||
|                     try { await arg.DeleteAsync(new RequestOptions() { RetryMode = RetryMode.AlwaysFail }).ConfigureAwait(false); } | ||||
|                     catch | ||||
|                     { | ||||
|                         // ignored | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|             return Task.CompletedTask; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,45 @@ | ||||
| using NadekoBot.Common; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Modules.Gambling.Common.WheelOfFortune | ||||
| { | ||||
|     public class WheelOfFortune | ||||
|     { | ||||
|         private static readonly NadekoRandom _rng = new NadekoRandom(); | ||||
|  | ||||
|         private static readonly ImmutableArray<string> _emojis = new string[] { | ||||
|             "⬆", | ||||
|             "↖", | ||||
|             "⬅", | ||||
|             "↙", | ||||
|             "⬇", | ||||
|             "↘", | ||||
|             "➡", | ||||
|             "↗" }.ToImmutableArray(); | ||||
|  | ||||
|         public static readonly ImmutableArray<float> Multipliers = new float[] { | ||||
|             1.7f, | ||||
|             1.5f, | ||||
|             0.2f, | ||||
|             0.1f, | ||||
|             0.3f, | ||||
|             0.5f, | ||||
|             1.2f, | ||||
|             2.4f, | ||||
|         }.ToImmutableArray(); | ||||
|  | ||||
|         public int Result { get; } | ||||
|         public string Emoji => _emojis[Result]; | ||||
|         public float Multiplier => Multipliers[Result]; | ||||
|  | ||||
|         public WheelOfFortune() | ||||
|         { | ||||
|             this.Result = _rng.Next(0, 8); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										91
									
								
								NadekoBot.Core/Modules/Gambling/CurrencyEventsCommands.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								NadekoBot.Core/Modules/Gambling/CurrencyEventsCommands.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,91 @@ | ||||
| using Discord; | ||||
| using Discord.Commands; | ||||
| using NadekoBot.Extensions; | ||||
| using NadekoBot.Core.Services; | ||||
| using System.Threading.Tasks; | ||||
| using Discord.WebSocket; | ||||
| using NadekoBot.Common.Attributes; | ||||
| using NadekoBot.Modules.Gambling.Common; | ||||
| using NadekoBot.Modules.Gambling.Services; | ||||
| using NadekoBot.Modules.Gambling.Common.CurrencyEvents; | ||||
|  | ||||
| namespace NadekoBot.Modules.Gambling | ||||
| { | ||||
|     public partial class Gambling | ||||
|     { | ||||
|         [Group] | ||||
|         public class CurrencyEventsCommands : NadekoSubmodule<CurrencyEventsService> | ||||
|         { | ||||
|             public enum CurrencyEvent | ||||
|             { | ||||
|                 Reaction, | ||||
|                 SneakyGameStatus | ||||
|             } | ||||
|  | ||||
|             private readonly DiscordSocketClient _client; | ||||
|             private readonly IBotConfigProvider _bc; | ||||
|             private readonly CurrencyService _cs; | ||||
|  | ||||
|             public CurrencyEventsCommands(DiscordSocketClient client, IBotConfigProvider bc, CurrencyService cs) | ||||
|             { | ||||
|                 _client = client; | ||||
|                 _bc = bc; | ||||
|                 _cs = cs; | ||||
|             } | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             [OwnerOnly] | ||||
|             public async Task StartEvent(CurrencyEvent e, int arg = -1) | ||||
|             { | ||||
|                 switch (e) | ||||
|                 { | ||||
|                     case CurrencyEvent.Reaction: | ||||
|                         await ReactionEvent(Context, arg).ConfigureAwait(false); | ||||
|                         break; | ||||
|                     case CurrencyEvent.SneakyGameStatus: | ||||
|                         await SneakyGameStatusEvent(Context, arg).ConfigureAwait(false); | ||||
|                         break; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             private async Task SneakyGameStatusEvent(ICommandContext context, int num) | ||||
|             { | ||||
|                 if (num < 10 || num > 600) | ||||
|                     num = 60; | ||||
|  | ||||
|                 var ev = new SneakyEvent(_cs, _client, _bc, num); | ||||
|                 if (!await _service.StartSneakyEvent(ev, context.Message, context)) | ||||
|                     return; | ||||
|                 try | ||||
|                 { | ||||
|                     var title = GetText("sneakygamestatus_title"); | ||||
|                     var desc = GetText("sneakygamestatus_desc",  | ||||
|                         Format.Bold(100.ToString()) + _bc.BotConfig.CurrencySign, | ||||
|                         Format.Bold(num.ToString())); | ||||
|                     await context.Channel.SendConfirmAsync(title, desc) | ||||
|                         .ConfigureAwait(false); | ||||
|                 } | ||||
|                 catch | ||||
|                 { | ||||
|                     // ignored | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             public async Task ReactionEvent(ICommandContext context, int amount) | ||||
|             { | ||||
|                 if (amount <= 0) | ||||
|                     amount = 100; | ||||
|  | ||||
|                 var title = GetText("reaction_title"); | ||||
|                 var desc = GetText("reaction_desc", _bc.BotConfig.CurrencySign, Format.Bold(amount.ToString()) + _bc.BotConfig.CurrencySign); | ||||
|                 var footer = GetText("reaction_footer", 24); | ||||
|                 var re = new ReactionEvent(_bc.BotConfig, _client, _cs, amount); | ||||
|                 var msg = await context.Channel.SendConfirmAsync(title, | ||||
|                         desc, footer: footer) | ||||
|                     .ConfigureAwait(false); | ||||
|                 await re.Start(msg, context); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										241
									
								
								NadekoBot.Core/Modules/Gambling/DiceRollCommands.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										241
									
								
								NadekoBot.Core/Modules/Gambling/DiceRollCommands.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,241 @@ | ||||
| using Discord; | ||||
| using Discord.Commands; | ||||
| using NadekoBot.Extensions; | ||||
| using NadekoBot.Core.Services; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.IO; | ||||
| using System.Linq; | ||||
| using System.Text.RegularExpressions; | ||||
| using System.Threading.Tasks; | ||||
| using NadekoBot.Common; | ||||
| using NadekoBot.Common.Attributes; | ||||
| using Image = ImageSharp.Image; | ||||
| using ImageSharp; | ||||
|  | ||||
| namespace NadekoBot.Modules.Gambling | ||||
| { | ||||
|     public partial class Gambling | ||||
|     { | ||||
|         [Group] | ||||
|         public class DriceRollCommands : NadekoSubmodule | ||||
|         { | ||||
|             private readonly Regex dndRegex  = new Regex(@"^(?<n1>\d+)d(?<n2>\d+)(?:\+(?<add>\d+))?(?:\-(?<sub>\d+))?$", RegexOptions.Compiled); | ||||
|             private readonly Regex fudgeRegex  = new Regex(@"^(?<n1>\d+)d(?:F|f)$", RegexOptions.Compiled); | ||||
|  | ||||
|             private readonly char[] _fateRolls = { '-', ' ', '+' }; | ||||
|             private readonly IImagesService _images; | ||||
|  | ||||
|             public DriceRollCommands(IImagesService images) | ||||
|             { | ||||
|                 _images = images; | ||||
|             } | ||||
|  | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             public async Task Roll() | ||||
|             { | ||||
|                 var rng = new NadekoRandom(); | ||||
|                 var gen = rng.Next(1, 101); | ||||
|  | ||||
|                 var num1 = gen / 10; | ||||
|                 var num2 = gen % 10; | ||||
|                 var imageStream = await Task.Run(() => | ||||
|                 { | ||||
|                     var ms = new MemoryStream(); | ||||
|                     new[] { GetDice(num1), GetDice(num2) }.Merge().SaveAsPng(ms); | ||||
|                     ms.Position = 0; | ||||
|                     return ms; | ||||
|                 }).ConfigureAwait(false); | ||||
|  | ||||
|                 await Context.Channel.SendFileAsync(imageStream,  | ||||
|                     "dice.png",  | ||||
|                     Context.User.Mention + " " + GetText("dice_rolled", Format.Code(gen.ToString()))).ConfigureAwait(false); | ||||
|             } | ||||
|  | ||||
|             public enum RollOrderType | ||||
|             { | ||||
|                 Ordered, | ||||
|                 Unordered | ||||
|             } | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [Priority(1)] | ||||
|             public async Task Roll(int num) | ||||
|             { | ||||
|                 await InternalRoll(num, true).ConfigureAwait(false); | ||||
|             } | ||||
|  | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [Priority(1)] | ||||
|             public async Task Rolluo(int num = 1) | ||||
|             { | ||||
|                 await InternalRoll(num, false).ConfigureAwait(false); | ||||
|             } | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [Priority(0)] | ||||
|             public async Task Roll(string arg) | ||||
|             { | ||||
|                 await InternallDndRoll(arg, true).ConfigureAwait(false); | ||||
|             } | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [Priority(0)] | ||||
|             public async Task Rolluo(string arg) | ||||
|             { | ||||
|                 await InternallDndRoll(arg, false).ConfigureAwait(false); | ||||
|             } | ||||
|  | ||||
|             private async Task InternalRoll(int num, bool ordered) | ||||
|             { | ||||
|                 if (num < 1 || num > 30) | ||||
|                 { | ||||
|                     await ReplyErrorLocalized("dice_invalid_number", 1, 30).ConfigureAwait(false); | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 var rng = new NadekoRandom(); | ||||
|  | ||||
|                 var dice = new List<Image<Rgba32>>(num); | ||||
|                 var values = new List<int>(num); | ||||
|                 for (var i = 0; i < num; i++) | ||||
|                 { | ||||
|                     var randomNumber = rng.Next(1, 7); | ||||
|                     var toInsert = dice.Count; | ||||
|                     if (ordered) | ||||
|                     { | ||||
|                         if (randomNumber == 6 || dice.Count == 0) | ||||
|                             toInsert = 0; | ||||
|                         else if (randomNumber != 1) | ||||
|                             for (var j = 0; j < dice.Count; j++) | ||||
|                             { | ||||
|                                 if (values[j] < randomNumber) | ||||
|                                 { | ||||
|                                     toInsert = j; | ||||
|                                     break; | ||||
|                                 } | ||||
|                             } | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         toInsert = dice.Count; | ||||
|                     } | ||||
|                     dice.Insert(toInsert, GetDice(randomNumber)); | ||||
|                     values.Insert(toInsert, randomNumber); | ||||
|                 } | ||||
|  | ||||
|                 var bitmap = dice.Merge(); | ||||
|                 var ms = new MemoryStream(); | ||||
|                 bitmap.SaveAsPng(ms); | ||||
|                 ms.Position = 0; | ||||
|                 await Context.Channel.SendFileAsync(ms, "dice.png", | ||||
|                     Context.User.Mention +  " " + | ||||
|                     GetText("dice_rolled_num", Format.Bold(values.Count.ToString())) + | ||||
|                     " " + GetText("total_average", | ||||
|                         Format.Bold(values.Sum().ToString()), | ||||
|                         Format.Bold((values.Sum() / (1.0f * values.Count)).ToString("N2")))).ConfigureAwait(false); | ||||
|             } | ||||
|  | ||||
|             private async Task InternallDndRoll(string arg, bool ordered) | ||||
|             { | ||||
|                 Match match; | ||||
|                 int n1; | ||||
|                 int n2; | ||||
|                 if ((match = fudgeRegex.Match(arg)).Length != 0 && | ||||
|                     int.TryParse(match.Groups["n1"].ToString(), out n1) && | ||||
|                     n1 > 0 && n1 < 500) | ||||
|                 { | ||||
|                     var rng = new NadekoRandom(); | ||||
|  | ||||
|                     var rolls = new List<char>(); | ||||
|  | ||||
|                     for (int i = 0; i < n1; i++) | ||||
|                     { | ||||
|                         rolls.Add(_fateRolls[rng.Next(0, _fateRolls.Length)]); | ||||
|                     } | ||||
|                     var embed = new EmbedBuilder().WithOkColor().WithDescription(Context.User.Mention + " " + GetText("dice_rolled_num", Format.Bold(n1.ToString()))) | ||||
|                         .AddField(efb => efb.WithName(Format.Bold("Result")) | ||||
|                             .WithValue(string.Join(" ", rolls.Select(c => Format.Code($"[{c}]"))))); | ||||
|                     await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); | ||||
|                 } | ||||
|                 else if ((match = dndRegex.Match(arg)).Length != 0) | ||||
|                 { | ||||
|                     var rng = new NadekoRandom(); | ||||
|                     if (int.TryParse(match.Groups["n1"].ToString(), out n1) && | ||||
|                         int.TryParse(match.Groups["n2"].ToString(), out n2) && | ||||
|                         n1 <= 50 && n2 <= 100000 && n1 > 0 && n2 > 0) | ||||
|                     { | ||||
|                         var add = 0; | ||||
|                         var sub = 0; | ||||
|                         int.TryParse(match.Groups["add"].Value, out add); | ||||
|                         int.TryParse(match.Groups["sub"].Value, out sub); | ||||
|  | ||||
|                         var arr = new int[n1]; | ||||
|                         for (int i = 0; i < n1; i++) | ||||
|                         { | ||||
|                             arr[i] = rng.Next(1, n2 + 1); | ||||
|                         } | ||||
|  | ||||
|                         var sum = arr.Sum(); | ||||
|                         var embed = new EmbedBuilder().WithOkColor().WithDescription(Context.User.Mention + " " +GetText("dice_rolled_num", n1) + $"`1 - {n2}`") | ||||
|                         .AddField(efb => efb.WithName(Format.Bold("Rolls")) | ||||
|                             .WithValue(string.Join(" ", (ordered ? arr.OrderBy(x => x).AsEnumerable() : arr).Select(x => Format.Code(x.ToString()))))) | ||||
|                         .AddField(efb => efb.WithName(Format.Bold("Sum")) | ||||
|                             .WithValue(sum + " + " + add + " - " + sub + " = " + (sum + add - sub))); | ||||
|                         await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             public async Task NRoll([Remainder] string range) | ||||
|             { | ||||
|                 int rolled; | ||||
|                 if (range.Contains("-")) | ||||
|                 { | ||||
|                     var arr = range.Split('-') | ||||
|                         .Take(2) | ||||
|                         .Select(int.Parse) | ||||
|                         .ToArray(); | ||||
|                     if (arr[0] > arr[1]) | ||||
|                     { | ||||
|                         await ReplyErrorLocalized("second_larger_than_first").ConfigureAwait(false); | ||||
|                         return; | ||||
|                     } | ||||
|                     rolled = new NadekoRandom().Next(arr[0], arr[1] + 1); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     rolled = new NadekoRandom().Next(0, int.Parse(range) + 1); | ||||
|                 } | ||||
|  | ||||
|                 await ReplyConfirmLocalized("dice_rolled", Format.Bold(rolled.ToString())).ConfigureAwait(false); | ||||
|             } | ||||
|  | ||||
|             private Image<Rgba32> GetDice(int num) | ||||
|             { | ||||
|                 if (num < 0 || num > 10) | ||||
|                     throw new ArgumentOutOfRangeException(nameof(num)); | ||||
|  | ||||
|                 if (num == 10) | ||||
|                 { | ||||
|                     var images = _images.Dice; | ||||
|                     using (var imgOneStream = images[1].ToStream()) | ||||
|                     using (var imgZeroStream = images[0].ToStream()) | ||||
|                     { | ||||
|                         var imgOne = Image.Load(imgOneStream); | ||||
|                         var imgZero = Image.Load(imgZeroStream); | ||||
|  | ||||
|                         return new[] { imgOne, imgZero }.Merge(); | ||||
|                     } | ||||
|                 } | ||||
|                 using (var die = _images.Dice[num].ToStream()) | ||||
|                 { | ||||
|                     return Image.Load(die); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										106
									
								
								NadekoBot.Core/Modules/Gambling/DrawCommands.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								NadekoBot.Core/Modules/Gambling/DrawCommands.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | ||||
| using Discord; | ||||
| using Discord.Commands; | ||||
| using NadekoBot.Extensions; | ||||
| using System; | ||||
| using System.Collections.Concurrent; | ||||
| using System.Collections.Generic; | ||||
| using System.IO; | ||||
| using System.Threading.Tasks; | ||||
| using NadekoBot.Common.Attributes; | ||||
| using NadekoBot.Modules.Gambling.Common; | ||||
| using Image = ImageSharp.Image; | ||||
| using ImageSharp; | ||||
|  | ||||
| namespace NadekoBot.Modules.Gambling | ||||
| { | ||||
|     public partial class Gambling | ||||
|     { | ||||
|         [Group] | ||||
|         public class DrawCommands : NadekoSubmodule | ||||
|         { | ||||
|             private static readonly ConcurrentDictionary<IGuild, Cards> _allDecks = new ConcurrentDictionary<IGuild, Cards>(); | ||||
|             private const string _cardsPath = "data/images/cards"; | ||||
|  | ||||
|              | ||||
|             private async Task<(Stream ImageStream, string ToSend)> InternalDraw(int num, ulong? guildId = null) | ||||
|             { | ||||
|                 if (num < 1 || num > 10) | ||||
|                     throw new ArgumentOutOfRangeException(nameof(num)); | ||||
|  | ||||
|                 Cards cards = guildId == null ? new Cards() : _allDecks.GetOrAdd(Context.Guild, (s) => new Cards()); | ||||
|                 var images = new List<Image<Rgba32>>(); | ||||
|                 var cardObjects = new List<Cards.Card>(); | ||||
|                 for (var i = 0; i < num; i++) | ||||
|                 { | ||||
|                     if (cards.CardPool.Count == 0 && i != 0) | ||||
|                     { | ||||
|                         try | ||||
|                         { | ||||
|                             await ReplyErrorLocalized("no_more_cards").ConfigureAwait(false); | ||||
|                         } | ||||
|                         catch | ||||
|                         { | ||||
|                             // ignored | ||||
|                         } | ||||
|                         break; | ||||
|                     } | ||||
|                     var currentCard = cards.DrawACard(); | ||||
|                     cardObjects.Add(currentCard); | ||||
|                     using (var stream = File.OpenRead(Path.Combine(_cardsPath, currentCard.ToString().ToLowerInvariant() + ".jpg").Replace(' ', '_'))) | ||||
|                         images.Add(Image.Load(stream)); | ||||
|                 } | ||||
|                 MemoryStream bitmapStream = new MemoryStream(); | ||||
|                 images.Merge().SaveAsPng(bitmapStream); | ||||
|                 bitmapStream.Position = 0; | ||||
|  | ||||
|                 var toSend = $"{Context.User.Mention}"; | ||||
|                 if (cardObjects.Count == 5) | ||||
|                     toSend += $" drew `{Cards.GetHandValue(cardObjects)}`"; | ||||
|  | ||||
|                 return (bitmapStream, toSend); | ||||
|             } | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             public async Task Draw(int num = 1) | ||||
|             { | ||||
|                 if (num < 1) | ||||
|                     num = 1; | ||||
|                 if (num > 10) | ||||
|                     num = 10; | ||||
|  | ||||
|                 var data = await InternalDraw(num, Context.Guild.Id).ConfigureAwait(false); | ||||
|                 await Context.Channel.SendFileAsync(data.ImageStream, num + " cards.jpg", data.ToSend).ConfigureAwait(false); | ||||
|             } | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             public async Task DrawNew(int num = 1) | ||||
|             { | ||||
|                 if (num < 1) | ||||
|                     num = 1; | ||||
|                 if (num > 10) | ||||
|                     num = 10; | ||||
|  | ||||
|                 var data = await InternalDraw(num).ConfigureAwait(false); | ||||
|                 await Context.Channel.SendFileAsync(data.ImageStream, num + " cards.jpg", data.ToSend).ConfigureAwait(false); | ||||
|             } | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             public async Task DeckShuffle() | ||||
|             { | ||||
|                 //var channel = (ITextChannel)Context.Channel; | ||||
|  | ||||
|                 _allDecks.AddOrUpdate(Context.Guild, | ||||
|                         (g) => new Cards(), | ||||
|                         (g, c) => | ||||
|                         { | ||||
|                             c.Restart(); | ||||
|                             return c; | ||||
|                         }); | ||||
|  | ||||
|                 await ReplyConfirmLocalized("deck_reshuffled").ConfigureAwait(false); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										133
									
								
								NadekoBot.Core/Modules/Gambling/FlipCoinCommands.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								NadekoBot.Core/Modules/Gambling/FlipCoinCommands.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,133 @@ | ||||
| using Discord; | ||||
| using Discord.Commands; | ||||
| using NadekoBot.Extensions; | ||||
| using NadekoBot.Core.Services; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Threading.Tasks; | ||||
| using NadekoBot.Common; | ||||
| using NadekoBot.Common.Attributes; | ||||
| using Image = ImageSharp.Image; | ||||
| using ImageSharp; | ||||
|  | ||||
| namespace NadekoBot.Modules.Gambling | ||||
| { | ||||
|     public partial class Gambling | ||||
|     { | ||||
|         [Group] | ||||
|         public class FlipCoinCommands : NadekoSubmodule | ||||
|         { | ||||
|             private readonly IImagesService _images; | ||||
|             private readonly IBotConfigProvider _bc; | ||||
|             private readonly CurrencyService _cs; | ||||
|  | ||||
|             private readonly NadekoRandom rng = new NadekoRandom(); | ||||
|  | ||||
|             public FlipCoinCommands(IImagesService images, CurrencyService cs, IBotConfigProvider bc) | ||||
|             { | ||||
|                 _images = images; | ||||
|                 _bc = bc; | ||||
|                 _cs = cs; | ||||
|             } | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             public async Task Flip(int count = 1) | ||||
|             { | ||||
|                 if (count == 1) | ||||
|                 { | ||||
|                     if (rng.Next(0, 2) == 1) | ||||
|                     { | ||||
|                         using (var heads = _images.Heads.ToStream()) | ||||
|                         { | ||||
|                             await Context.Channel.SendFileAsync(heads, "heads.jpg", Context.User.Mention + " " + GetText("flipped", Format.Bold(GetText("heads")))).ConfigureAwait(false); | ||||
|                         } | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         using (var tails = _images.Tails.ToStream()) | ||||
|                         { | ||||
|                             await Context.Channel.SendFileAsync(tails, "tails.jpg", Context.User.Mention + " " + GetText("flipped", Format.Bold(GetText("tails")))).ConfigureAwait(false); | ||||
|                         } | ||||
|                     } | ||||
|                     return; | ||||
|                 } | ||||
|                 if (count > 10 || count < 1) | ||||
|                 { | ||||
|                     await ReplyErrorLocalized("flip_invalid", 10).ConfigureAwait(false); | ||||
|                     return; | ||||
|                 } | ||||
|                 var imgs = new Image<Rgba32>[count]; | ||||
|                 for (var i = 0; i < count; i++) | ||||
|                 { | ||||
|                     using (var heads = _images.Heads.ToStream()) | ||||
|                     using (var tails = _images.Tails.ToStream()) | ||||
|                     { | ||||
|                         if (rng.Next(0, 10) < 5) | ||||
|                         { | ||||
|                             imgs[i] = Image.Load(heads); | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             imgs[i] = Image.Load(tails); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 await Context.Channel.SendFileAsync(imgs.Merge().ToStream(), $"{count} coins.png").ConfigureAwait(false); | ||||
|             } | ||||
|  | ||||
|             public enum BetFlipGuess | ||||
|             { | ||||
|                 H = 1, | ||||
|                 Head = 1, | ||||
|                 Heads = 1, | ||||
|                 T = 2, | ||||
|                 Tail = 2, | ||||
|                 Tails = 2 | ||||
|             } | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             public async Task Betflip(int amount, BetFlipGuess guess) | ||||
|             { | ||||
|                 if (amount < _bc.BotConfig.MinimumBetAmount) | ||||
|                 { | ||||
|                     await ReplyErrorLocalized("min_bet_limit", _bc.BotConfig.MinimumBetAmount + _bc.BotConfig.CurrencySign).ConfigureAwait(false); | ||||
|                     return; | ||||
|                 } | ||||
|                 var removed = await _cs.RemoveAsync(Context.User, "Betflip Gamble", amount, false).ConfigureAwait(false); | ||||
|                 if (!removed) | ||||
|                 { | ||||
|                     await ReplyErrorLocalized("not_enough", _bc.BotConfig.CurrencyPluralName).ConfigureAwait(false); | ||||
|                     return; | ||||
|                 } | ||||
|                 BetFlipGuess result; | ||||
|                 IEnumerable<byte> imageToSend; | ||||
|                 if (rng.Next(0, 2) == 1) | ||||
|                 { | ||||
|                     imageToSend = _images.Heads; | ||||
|                     result = BetFlipGuess.Heads; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     imageToSend = _images.Tails; | ||||
|                     result = BetFlipGuess.Tails; | ||||
|                 } | ||||
|  | ||||
|                 string str; | ||||
|                 if (guess == result) | ||||
|                 {  | ||||
|                     var toWin = (int)Math.Round(amount * _bc.BotConfig.BetflipMultiplier); | ||||
|                     str = Context.User.Mention + " " + GetText("flip_guess", toWin + _bc.BotConfig.CurrencySign); | ||||
|                     await _cs.AddAsync(Context.User, "Betflip Gamble", toWin, false).ConfigureAwait(false); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     str = Context.User.Mention + " " + GetText("better_luck"); | ||||
|                 } | ||||
|                 using (var toSend = imageToSend.ToStream()) | ||||
|                 { | ||||
|                     await Context.Channel.SendFileAsync(toSend, "result.png", str).ConfigureAwait(false); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										366
									
								
								NadekoBot.Core/Modules/Gambling/FlowerShopCommands.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										366
									
								
								NadekoBot.Core/Modules/Gambling/FlowerShopCommands.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,366 @@ | ||||
| using Discord; | ||||
| using Discord.Commands; | ||||
| using Discord.WebSocket; | ||||
| using Microsoft.EntityFrameworkCore; | ||||
| using NadekoBot.Extensions; | ||||
| using NadekoBot.Core.Services; | ||||
| using NadekoBot.Core.Services.Database.Models; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Threading.Tasks; | ||||
| using NadekoBot.Common; | ||||
| using NadekoBot.Common.Attributes; | ||||
| using NadekoBot.Common.Collections; | ||||
|  | ||||
| namespace NadekoBot.Modules.Gambling | ||||
| { | ||||
|     public partial class Gambling | ||||
|     { | ||||
|         [Group] | ||||
|         public class FlowerShopCommands : NadekoSubmodule | ||||
|         { | ||||
|             private readonly IBotConfigProvider _bc; | ||||
|             private readonly DbService _db; | ||||
|             private readonly CurrencyService _cs; | ||||
|             private readonly DiscordSocketClient _client; | ||||
|  | ||||
|             public enum Role | ||||
|             { | ||||
|                 Role | ||||
|             } | ||||
|  | ||||
|             public enum List | ||||
|             { | ||||
|                 List | ||||
|             } | ||||
|  | ||||
|             public FlowerShopCommands(IBotConfigProvider bc, DbService db, CurrencyService cs, DiscordSocketClient client) | ||||
|             { | ||||
|                 _db = db; | ||||
|                 _bc = bc; | ||||
|                 _cs = cs; | ||||
|                 _client = client; | ||||
|             } | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             public async Task Shop(int page = 1) | ||||
|             { | ||||
|                 if (--page < 0) | ||||
|                     return; | ||||
|                 List<ShopEntry> entries; | ||||
|                 using (var uow = _db.UnitOfWork) | ||||
|                 { | ||||
|                     entries = new IndexedCollection<ShopEntry>(uow.GuildConfigs.For(Context.Guild.Id,  | ||||
|                         set => set.Include(x => x.ShopEntries) | ||||
|                                   .ThenInclude(x => x.Items)).ShopEntries); | ||||
|                 } | ||||
|  | ||||
|                 await Context.Channel.SendPaginatedConfirmAsync(_client, page, (curPage) => | ||||
|                 { | ||||
|                     var theseEntries = entries.Skip(curPage * 9).Take(9).ToArray(); | ||||
|  | ||||
|                     if (!theseEntries.Any()) | ||||
|                         return new EmbedBuilder().WithErrorColor() | ||||
|                             .WithDescription(GetText("shop_none")); | ||||
|                     var embed = new EmbedBuilder().WithOkColor() | ||||
|                         .WithTitle(GetText("shop", _bc.BotConfig.CurrencySign)); | ||||
|  | ||||
|                     for (int i = 0; i < theseEntries.Length; i++) | ||||
|                     { | ||||
|                         var entry = theseEntries[i]; | ||||
|                         embed.AddField(efb => efb.WithName($"#{curPage * 9 + i + 1} - {entry.Price}{_bc.BotConfig.CurrencySign}").WithValue(EntryToString(entry)).WithIsInline(true)); | ||||
|                     } | ||||
|                     return embed; | ||||
|                 }, entries.Count / 9, true); | ||||
|             } | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             public async Task Buy(int index, [Remainder]string message = null) | ||||
|             { | ||||
|                 index -= 1; | ||||
|                 if (index < 0) | ||||
|                     return; | ||||
|                 ShopEntry entry; | ||||
|                 using (var uow = _db.UnitOfWork) | ||||
|                 { | ||||
|                     var config = uow.GuildConfigs.For(Context.Guild.Id, set => set | ||||
|                         .Include(x => x.ShopEntries) | ||||
|                         .ThenInclude(x => x.Items)); | ||||
|                     var entries = new IndexedCollection<ShopEntry>(config.ShopEntries); | ||||
|                     entry = entries.ElementAtOrDefault(index); | ||||
|                     uow.Complete(); | ||||
|                 } | ||||
|  | ||||
|                 if (entry == null) | ||||
|                 { | ||||
|                     await ReplyErrorLocalized("shop_item_not_found").ConfigureAwait(false); | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 if (entry.Type == ShopEntryType.Role) | ||||
|                 { | ||||
|                     var guser = (IGuildUser)Context.User; | ||||
|                     var role = Context.Guild.GetRole(entry.RoleId); | ||||
|  | ||||
|                     if (role == null) | ||||
|                     { | ||||
|                         await ReplyErrorLocalized("shop_role_not_found").ConfigureAwait(false); | ||||
|                         return; | ||||
|                     } | ||||
|  | ||||
|                     if (await _cs.RemoveAsync(Context.User.Id, $"Shop purchase - {entry.Type}", entry.Price)) | ||||
|                     { | ||||
|                         try | ||||
|                         { | ||||
|                             await guser.AddRoleAsync(role).ConfigureAwait(false); | ||||
|                         } | ||||
|                         catch (Exception ex) | ||||
|                         { | ||||
|                             _log.Warn(ex); | ||||
|                             await _cs.AddAsync(Context.User.Id, $"Shop error refund", entry.Price); | ||||
|                             await ReplyErrorLocalized("shop_role_purchase_error").ConfigureAwait(false); | ||||
|                             return; | ||||
|                         } | ||||
|                         await _cs.AddAsync(entry.AuthorId, $"Shop sell item - {entry.Type}", GetProfitAmount(entry.Price)); | ||||
|                         await ReplyConfirmLocalized("shop_role_purchase", Format.Bold(role.Name)).ConfigureAwait(false); | ||||
|                         return; | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         await ReplyErrorLocalized("not_enough", _bc.BotConfig.CurrencySign).ConfigureAwait(false); | ||||
|                         return; | ||||
|                     } | ||||
|                 } | ||||
|                 else if (entry.Type == ShopEntryType.List) | ||||
|                 { | ||||
|                     if (entry.Items.Count == 0) | ||||
|                     { | ||||
|                         await ReplyErrorLocalized("out_of_stock").ConfigureAwait(false); | ||||
|                         return; | ||||
|                     } | ||||
|  | ||||
|                     var item = entry.Items.ToArray()[new NadekoRandom().Next(0, entry.Items.Count)]; | ||||
|  | ||||
|                     if (await _cs.RemoveAsync(Context.User.Id, $"Shop purchase - {entry.Type}", entry.Price)) | ||||
|                     { | ||||
|                         int removed; | ||||
|                         using (var uow = _db.UnitOfWork) | ||||
|                         { | ||||
|                             var x = uow._context.Set<ShopEntryItem>().Remove(item); | ||||
|  | ||||
|                             removed = uow.Complete(); | ||||
|                         } | ||||
|                         try | ||||
|                         { | ||||
|                             await (await Context.User.GetOrCreateDMChannelAsync()) | ||||
|                                 .EmbedAsync(new EmbedBuilder().WithOkColor() | ||||
|                                 .WithTitle(GetText("shop_purchase", Context.Guild.Name)) | ||||
|                                 .AddField(efb => efb.WithName(GetText("item")).WithValue(item.Text).WithIsInline(false)) | ||||
|                                 .AddField(efb => efb.WithName(GetText("price")).WithValue(entry.Price.ToString()).WithIsInline(true)) | ||||
|                                 .AddField(efb => efb.WithName(GetText("name")).WithValue(entry.Name).WithIsInline(true))) | ||||
|                                 .ConfigureAwait(false); | ||||
|  | ||||
|                             await _cs.AddAsync(entry.AuthorId, | ||||
|                                     $"Shop sell item - {entry.Name}", | ||||
|                                     GetProfitAmount(entry.Price)).ConfigureAwait(false); | ||||
|                         } | ||||
|                         catch | ||||
|                         { | ||||
|                             using (var uow = _db.UnitOfWork) | ||||
|                             { | ||||
|                                 uow._context.Set<ShopEntryItem>().Add(item); | ||||
|                                 uow.Complete(); | ||||
|  | ||||
|                                 await _cs.AddAsync(Context.User.Id,  | ||||
|                                     $"Shop error refund - {entry.Name}",  | ||||
|                                     entry.Price,  | ||||
|                                     uow).ConfigureAwait(false); | ||||
|                             } | ||||
|                             await ReplyErrorLocalized("shop_buy_error").ConfigureAwait(false); | ||||
|                             return; | ||||
|                         } | ||||
|                         await ReplyConfirmLocalized("shop_item_purchase").ConfigureAwait(false); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         await ReplyErrorLocalized("not_enough", _bc.BotConfig.CurrencySign).ConfigureAwait(false); | ||||
|                         return; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|  | ||||
|             private long GetProfitAmount(int price) =>  | ||||
|                 (int)(Math.Ceiling(0.90 * price)); | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             [RequireUserPermission(GuildPermission.Administrator)] | ||||
|             [RequireBotPermission(GuildPermission.ManageRoles)] | ||||
|             public async Task ShopAdd(Role _, int price, [Remainder] IRole role) | ||||
|             { | ||||
|                 var entry = new ShopEntry() | ||||
|                 { | ||||
|                     Name = "-", | ||||
|                     Price = price, | ||||
|                     Type = ShopEntryType.Role, | ||||
|                     AuthorId = Context.User.Id, | ||||
|                     RoleId = role.Id, | ||||
|                     RoleName = role.Name | ||||
|                 }; | ||||
|                 using (var uow = _db.UnitOfWork) | ||||
|                 { | ||||
|                     var entries = new IndexedCollection<ShopEntry>(uow.GuildConfigs.For(Context.Guild.Id, | ||||
|                         set => set.Include(x => x.ShopEntries) | ||||
|                                   .ThenInclude(x => x.Items)).ShopEntries) | ||||
|                     { | ||||
|                         entry | ||||
|                     }; | ||||
|                     uow.GuildConfigs.For(Context.Guild.Id, set => set).ShopEntries = entries; | ||||
|                     uow.Complete(); | ||||
|                 } | ||||
|                 await Context.Channel.EmbedAsync(EntryToEmbed(entry) | ||||
|                     .WithTitle(GetText("shop_item_add"))); | ||||
|             } | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             [RequireUserPermission(GuildPermission.Administrator)] | ||||
|             public async Task ShopAdd(List _, int price, [Remainder]string name) | ||||
|             { | ||||
|                 var entry = new ShopEntry() | ||||
|                 { | ||||
|                     Name = name.TrimTo(100), | ||||
|                     Price = price, | ||||
|                     Type = ShopEntryType.List, | ||||
|                     AuthorId = Context.User.Id, | ||||
|                     Items = new HashSet<ShopEntryItem>(), | ||||
|                 }; | ||||
|                 using (var uow = _db.UnitOfWork) | ||||
|                 { | ||||
|                     var entries = new IndexedCollection<ShopEntry>(uow.GuildConfigs.For(Context.Guild.Id, | ||||
|                         set => set.Include(x => x.ShopEntries) | ||||
|                                   .ThenInclude(x => x.Items)).ShopEntries) | ||||
|                     { | ||||
|                         entry | ||||
|                     }; | ||||
|                     uow.GuildConfigs.For(Context.Guild.Id, set => set).ShopEntries = entries; | ||||
|                     uow.Complete(); | ||||
|                 } | ||||
|                 await Context.Channel.EmbedAsync(EntryToEmbed(entry) | ||||
|                     .WithTitle(GetText("shop_item_add"))); | ||||
|             } | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             [RequireUserPermission(GuildPermission.Administrator)] | ||||
|             public async Task ShopListAdd(int index, [Remainder] string itemText) | ||||
|             { | ||||
|                 index -= 1; | ||||
|                 if (index < 0) | ||||
|                     return; | ||||
|                 var item = new ShopEntryItem() | ||||
|                 { | ||||
|                     Text = itemText | ||||
|                 }; | ||||
|                 ShopEntry entry; | ||||
|                 bool rightType = false; | ||||
|                 bool added = false; | ||||
|                 using (var uow = _db.UnitOfWork) | ||||
|                 { | ||||
|                     var entries = new IndexedCollection<ShopEntry>(uow.GuildConfigs.For(Context.Guild.Id, | ||||
|                         set => set.Include(x => x.ShopEntries) | ||||
|                                   .ThenInclude(x => x.Items)).ShopEntries); | ||||
|                     entry = entries.ElementAtOrDefault(index); | ||||
|                     if (entry != null && (rightType = (entry.Type == ShopEntryType.List))) | ||||
|                     { | ||||
|                         if (added = entry.Items.Add(item)) | ||||
|                         { | ||||
|                             uow.Complete(); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 if (entry == null) | ||||
|                     await ReplyErrorLocalized("shop_item_not_found").ConfigureAwait(false); | ||||
|                 else if (!rightType) | ||||
|                     await ReplyErrorLocalized("shop_item_wrong_type").ConfigureAwait(false); | ||||
|                 else if (added == false) | ||||
|                     await ReplyErrorLocalized("shop_list_item_not_unique").ConfigureAwait(false); | ||||
|                 else | ||||
|                     await ReplyConfirmLocalized("shop_list_item_added").ConfigureAwait(false); | ||||
|             } | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             [RequireUserPermission(GuildPermission.Administrator)] | ||||
|             public async Task ShopRemove(int index) | ||||
|             { | ||||
|                 index -= 1; | ||||
|                 if (index < 0) | ||||
|                     return; | ||||
|                 ShopEntry removed; | ||||
|                 using (var uow = _db.UnitOfWork) | ||||
|                 { | ||||
|                     var config = uow.GuildConfigs.For(Context.Guild.Id, set => set | ||||
|                         .Include(x => x.ShopEntries) | ||||
|                         .ThenInclude(x => x.Items)); | ||||
|  | ||||
|                     var entries = new IndexedCollection<ShopEntry>(config.ShopEntries); | ||||
|                     removed = entries.ElementAtOrDefault(index); | ||||
|                     if (removed != null) | ||||
|                     { | ||||
|                         entries.Remove(removed); | ||||
|  | ||||
|                         config.ShopEntries = entries; | ||||
|                         uow.Complete(); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 if (removed == null) | ||||
|                     await ReplyErrorLocalized("shop_item_not_found").ConfigureAwait(false); | ||||
|                 else | ||||
|                     await Context.Channel.EmbedAsync(EntryToEmbed(removed) | ||||
|                         .WithTitle(GetText("shop_item_rm"))); | ||||
|             } | ||||
|  | ||||
|             public EmbedBuilder EntryToEmbed(ShopEntry entry) | ||||
|             { | ||||
|                 var embed = new EmbedBuilder().WithOkColor(); | ||||
|  | ||||
|                 if (entry.Type == ShopEntryType.Role) | ||||
|                     return embed.AddField(efb => efb.WithName(GetText("name")).WithValue(GetText("shop_role", Format.Bold(Context.Guild.GetRole(entry.RoleId)?.Name ?? "MISSING_ROLE"))).WithIsInline(true)) | ||||
|                             .AddField(efb => efb.WithName(GetText("price")).WithValue(entry.Price.ToString()).WithIsInline(true)) | ||||
|                             .AddField(efb => efb.WithName(GetText("type")).WithValue(entry.Type.ToString()).WithIsInline(true)); | ||||
|                 else if (entry.Type == ShopEntryType.List) | ||||
|                     return embed.AddField(efb => efb.WithName(GetText("name")).WithValue(entry.Name).WithIsInline(true)) | ||||
|                             .AddField(efb => efb.WithName(GetText("price")).WithValue(entry.Price.ToString()).WithIsInline(true)) | ||||
|                             .AddField(efb => efb.WithName(GetText("type")).WithValue(GetText("random_unique_item")).WithIsInline(true)); | ||||
|                 //else if (entry.Type == ShopEntryType.Infinite_List) | ||||
|                 //    return embed.AddField(efb => efb.WithName(GetText("name")).WithValue(GetText("shop_role", Format.Bold(entry.RoleName))).WithIsInline(true)) | ||||
|                 //            .AddField(efb => efb.WithName(GetText("price")).WithValue(entry.Price.ToString()).WithIsInline(true)) | ||||
|                 //            .AddField(efb => efb.WithName(GetText("type")).WithValue(entry.Type.ToString()).WithIsInline(true)); | ||||
|                 else return null; | ||||
|             } | ||||
|  | ||||
|             public string EntryToString(ShopEntry entry) | ||||
|             { | ||||
|                 if (entry.Type == ShopEntryType.Role) | ||||
|                 { | ||||
|                     return GetText("shop_role", Format.Bold(Context.Guild.GetRole(entry.RoleId)?.Name ?? "MISSING_ROLE")); | ||||
|                 } | ||||
|                 else if (entry.Type == ShopEntryType.List) | ||||
|                 { | ||||
|                     return GetText("unique_items_left", entry.Items.Count) + "\n" + entry.Name; | ||||
|                 } | ||||
|                 //else if (entry.Type == ShopEntryType.Infinite_List) | ||||
|                 //{ | ||||
|  | ||||
|                 //} | ||||
|                 return ""; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										295
									
								
								NadekoBot.Core/Modules/Gambling/Gambling.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										295
									
								
								NadekoBot.Core/Modules/Gambling/Gambling.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,295 @@ | ||||
| using Discord; | ||||
| using Discord.Commands; | ||||
| using NadekoBot.Extensions; | ||||
| using System.Linq; | ||||
| using System.Threading.Tasks; | ||||
| using NadekoBot.Core.Services; | ||||
| using NadekoBot.Core.Services.Database.Models; | ||||
| using System.Collections.Generic; | ||||
| using NadekoBot.Common; | ||||
| using NadekoBot.Common.Attributes; | ||||
|  | ||||
| namespace NadekoBot.Modules.Gambling | ||||
| { | ||||
|     public partial class Gambling : NadekoTopLevelModule | ||||
|     { | ||||
|         private readonly IBotConfigProvider _bc; | ||||
|         private readonly DbService _db; | ||||
|         private readonly CurrencyService _currency; | ||||
|  | ||||
|         private string CurrencyName => _bc.BotConfig.CurrencyName; | ||||
|         private string CurrencyPluralName => _bc.BotConfig.CurrencyPluralName; | ||||
|         private string CurrencySign => _bc.BotConfig.CurrencySign; | ||||
|  | ||||
|         public Gambling(IBotConfigProvider bc, DbService db, CurrencyService currency) | ||||
|         { | ||||
|             _bc = bc; | ||||
|             _db = db; | ||||
|             _currency = currency; | ||||
|         } | ||||
|  | ||||
|         public long GetCurrency(ulong id) | ||||
|         { | ||||
|             using (var uow = _db.UnitOfWork) | ||||
|             { | ||||
|                 return uow.Currency.GetUserCurrency(id); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [RequireContext(ContextType.Guild)] | ||||
|         public async Task Raffle([Remainder] IRole role = null) | ||||
|         { | ||||
|             role = role ?? Context.Guild.EveryoneRole; | ||||
|  | ||||
|             var members = (await role.GetMembersAsync()).Where(u => u.Status != UserStatus.Offline); | ||||
|             var membersArray = members as IUser[] ?? members.ToArray(); | ||||
|             if (membersArray.Length == 0) | ||||
|             { | ||||
|  | ||||
|             } | ||||
|             var usr = membersArray[new NadekoRandom().Next(0, membersArray.Length)]; | ||||
|             await Context.Channel.SendConfirmAsync("🎟 "+ GetText("raffled_user"), $"**{usr.Username}#{usr.Discriminator}**", footer: $"ID: {usr.Id}").ConfigureAwait(false); | ||||
|         } | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [Priority(1)] | ||||
|         public async Task Cash([Remainder] IUser user = null) | ||||
|         { | ||||
|             if(user == null) | ||||
|                 await ConfirmLocalized("has", Format.Bold(Context.User.ToString()), $"{GetCurrency(Context.User.Id)} {CurrencySign}").ConfigureAwait(false); | ||||
|             else | ||||
|                 await ReplyConfirmLocalized("has", Format.Bold(user.ToString()), $"{GetCurrency(user.Id)} {CurrencySign}").ConfigureAwait(false); | ||||
|         } | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [Priority(0)] | ||||
|         public async Task Cash(ulong userId) | ||||
|         { | ||||
|             await ReplyConfirmLocalized("has", Format.Code(userId.ToString()), $"{GetCurrency(userId)} {CurrencySign}").ConfigureAwait(false); | ||||
|         } | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [RequireContext(ContextType.Guild)] | ||||
|         public async Task Give(long amount, [Remainder] IGuildUser receiver) | ||||
|         { | ||||
|             if (amount <= 0 || Context.User.Id == receiver.Id) | ||||
|                 return; | ||||
|             var success = await _currency.RemoveAsync((IGuildUser)Context.User, $"Gift to {receiver.Username} ({receiver.Id}).", amount, false).ConfigureAwait(false); | ||||
|             if (!success) | ||||
|             { | ||||
|                 await ReplyErrorLocalized("not_enough", CurrencyPluralName).ConfigureAwait(false); | ||||
|                 return; | ||||
|             } | ||||
|             await _currency.AddAsync(receiver, $"Gift from {Context.User.Username} ({Context.User.Id}).", amount, true).ConfigureAwait(false); | ||||
|             await ReplyConfirmLocalized("gifted", amount + CurrencySign, Format.Bold(receiver.ToString())) | ||||
|                 .ConfigureAwait(false); | ||||
|         } | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [RequireContext(ContextType.Guild)] | ||||
|         [OwnerOnly] | ||||
|         [Priority(0)] | ||||
|         public Task Award(int amount, [Remainder] IGuildUser usr) => | ||||
|             Award(amount, usr.Id); | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [OwnerOnly] | ||||
|         [Priority(1)] | ||||
|         public async Task Award(int amount, ulong usrId) | ||||
|         { | ||||
|             if (amount <= 0) | ||||
|                 return; | ||||
|  | ||||
|             await _currency.AddAsync(usrId, $"Awarded by bot owner. ({Context.User.Username}/{Context.User.Id})", amount).ConfigureAwait(false); | ||||
|             await ReplyConfirmLocalized("awarded", amount + CurrencySign, $"<@{usrId}>").ConfigureAwait(false); | ||||
|         } | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [RequireContext(ContextType.Guild)] | ||||
|         [OwnerOnly] | ||||
|         [Priority(2)] | ||||
|         public async Task Award(int amount, [Remainder] IRole role) | ||||
|         { | ||||
|             var users = (await Context.Guild.GetUsersAsync()) | ||||
|                                .Where(u => u.GetRoles().Contains(role)) | ||||
|                                .ToList(); | ||||
|             await Task.WhenAll(users.Select(u => _currency.AddAsync(u.Id, | ||||
|                                                       $"Awarded by bot owner to **{role.Name}** role. ({Context.User.Username}/{Context.User.Id})", | ||||
|                                                       amount))) | ||||
|                          .ConfigureAwait(false); | ||||
|  | ||||
|             await ReplyConfirmLocalized("mass_award",  | ||||
|                 amount + CurrencySign,  | ||||
|                 Format.Bold(users.Count.ToString()),  | ||||
|                 Format.Bold(role.Name)).ConfigureAwait(false); | ||||
|         } | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [RequireContext(ContextType.Guild)] | ||||
|         [OwnerOnly] | ||||
|         public async Task Take(long amount, [Remainder] IGuildUser user) | ||||
|         { | ||||
|             if (amount <= 0) | ||||
|                 return; | ||||
|  | ||||
|             if (await _currency.RemoveAsync(user, $"Taken by bot owner.({Context.User.Username}/{Context.User.Id})", amount, true).ConfigureAwait(false)) | ||||
|                 await ReplyConfirmLocalized("take", amount+CurrencySign, Format.Bold(user.ToString())).ConfigureAwait(false); | ||||
|             else | ||||
|                 await ReplyErrorLocalized("take_fail", amount + CurrencySign, Format.Bold(user.ToString()), CurrencyPluralName).ConfigureAwait(false); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [OwnerOnly] | ||||
|         public async Task Take(long amount, [Remainder] ulong usrId) | ||||
|         { | ||||
|             if (amount <= 0) | ||||
|                 return; | ||||
|  | ||||
|             if (await _currency.RemoveAsync(usrId, $"Taken by bot owner.({Context.User.Username}/{Context.User.Id})", amount).ConfigureAwait(false)) | ||||
|                 await ReplyConfirmLocalized("take", amount + CurrencySign, $"<@{usrId}>").ConfigureAwait(false); | ||||
|             else | ||||
|                 await ReplyErrorLocalized("take_fail", amount + CurrencySign, Format.Code(usrId.ToString()), CurrencyPluralName).ConfigureAwait(false); | ||||
|         } | ||||
|  | ||||
|         //[NadekoCommand, Usage, Description, Aliases] | ||||
|         //[OwnerOnly] | ||||
|         //public Task BrTest(int tests = 1000) | ||||
|         //{ | ||||
|         //    var t = Task.Run(async () => | ||||
|         //    { | ||||
|         //        if (tests <= 0) | ||||
|         //            return; | ||||
|         //        //multi vs how many times it occured | ||||
|         //        var dict = new Dictionary<int, int>(); | ||||
|         //        var generator = new NadekoRandom(); | ||||
|         //        for (int i = 0; i < tests; i++) | ||||
|         //        { | ||||
|         //            var rng = generator.Next(0, 101); | ||||
|         //            var mult = 0; | ||||
|         //            if (rng < 67) | ||||
|         //            { | ||||
|         //                mult = 0; | ||||
|         //            } | ||||
|         //            else if (rng < 91) | ||||
|         //            { | ||||
|         //                mult = 2; | ||||
|         //            } | ||||
|         //            else if (rng < 100) | ||||
|         //            { | ||||
|         //                mult = 4; | ||||
|         //            } | ||||
|         //            else | ||||
|         //                mult = 10; | ||||
|  | ||||
|         //            if (dict.ContainsKey(mult)) | ||||
|         //                dict[mult] += 1; | ||||
|         //            else | ||||
|         //                dict.Add(mult, 1); | ||||
|         //        } | ||||
|  | ||||
|         //        var sb = new StringBuilder(); | ||||
|         //        const int bet = 1; | ||||
|         //        int payout = 0; | ||||
|         //        foreach (var key in dict.Keys.OrderByDescending(x => x)) | ||||
|         //        { | ||||
|         //            sb.AppendLine($"x{key} occured {dict[key]} times. {dict[key] * 1.0f / tests * 100}%"); | ||||
|         //            payout += key * dict[key]; | ||||
|         //        } | ||||
|         //        try | ||||
|         //        { | ||||
|         //            await Context.Channel.SendConfirmAsync("BetRoll Test Results", sb.ToString(), | ||||
|         //                footer: $"Total Bet: {tests * bet} | Payout: {payout * bet} | {payout * 1.0f / tests * 100}%"); | ||||
|         //        } | ||||
|         //        catch { } | ||||
|  | ||||
|         //    }); | ||||
|         //    return Task.CompletedTask; | ||||
|         //} | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         public async Task BetRoll(long amount) | ||||
|         { | ||||
|             if (amount < 1) | ||||
|                 return; | ||||
|  | ||||
|             if (!await _currency.RemoveAsync(Context.User, "Betroll Gamble", amount, false).ConfigureAwait(false)) | ||||
|             { | ||||
|                 await ReplyErrorLocalized("not_enough", CurrencyPluralName).ConfigureAwait(false); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             var rnd = new NadekoRandom().Next(0, 101); | ||||
|             var str = Context.User.Mention + Format.Code(GetText("roll", rnd)); | ||||
|             if (rnd < 67) | ||||
|             { | ||||
|                 str += GetText("better_luck"); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (rnd < 91) | ||||
|                 { | ||||
|                     str += GetText("br_win", (amount * _bc.BotConfig.Betroll67Multiplier) + CurrencySign, 66); | ||||
|                     await _currency.AddAsync(Context.User, "Betroll Gamble", | ||||
|                         (int) (amount * _bc.BotConfig.Betroll67Multiplier), false).ConfigureAwait(false); | ||||
|                 } | ||||
|                 else if (rnd < 100) | ||||
|                 { | ||||
|                     str += GetText("br_win", (amount * _bc.BotConfig.Betroll91Multiplier) + CurrencySign, 90); | ||||
|                     await _currency.AddAsync(Context.User, "Betroll Gamble", | ||||
|                         (int) (amount * _bc.BotConfig.Betroll91Multiplier), false).ConfigureAwait(false); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     str += GetText("br_win", (amount * _bc.BotConfig.Betroll100Multiplier) + CurrencySign, 100) + " 👑"; | ||||
|                     await _currency.AddAsync(Context.User, "Betroll Gamble", | ||||
|                         (int) (amount * _bc.BotConfig.Betroll100Multiplier), false).ConfigureAwait(false); | ||||
|                 } | ||||
|             } | ||||
|             await Context.Channel.SendConfirmAsync(str).ConfigureAwait(false); | ||||
|         } | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         public async Task Leaderboard(int page = 1) | ||||
|         { | ||||
|             if (page < 1) | ||||
|                 return; | ||||
|  | ||||
|             List<Currency> richest; | ||||
|             using (var uow = _db.UnitOfWork) | ||||
|             { | ||||
|                 richest = uow.Currency.GetTopRichest(9, 9 * (page - 1)).ToList(); | ||||
|             } | ||||
|  | ||||
|             var embed = new EmbedBuilder() | ||||
|                 .WithOkColor() | ||||
|                 .WithTitle(CurrencySign + | ||||
|                            " " + GetText("leaderboard")) | ||||
|                 .WithFooter(efb => efb.WithText(GetText("page", page))); | ||||
|  | ||||
|             if (!richest.Any()) | ||||
|             { | ||||
|                 embed.WithDescription(GetText("no_users_found")); | ||||
|                 await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             for (var i = 0; i < richest.Count; i++) | ||||
|             { | ||||
|                 var x = richest[i]; | ||||
|                 var usr = await Context.Guild.GetUserAsync(x.UserId).ConfigureAwait(false); | ||||
|                 var usrStr = usr == null  | ||||
|                     ? x.UserId.ToString()  | ||||
|                     : usr.Username?.TrimTo(20, true); | ||||
|  | ||||
|                 var j = i; | ||||
|                 embed.AddField(efb => efb.WithName("#" + (9 * (page - 1) + j + 1) + " " + usrStr) | ||||
|                                          .WithValue(x.Amount.ToString() + " " + CurrencySign) | ||||
|                                          .WithIsInline(true)); | ||||
|             } | ||||
|  | ||||
|             await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,21 @@ | ||||
| using System.Threading.Tasks; | ||||
| using NadekoBot.Core.Services; | ||||
| using System.Collections.Concurrent; | ||||
| using NadekoBot.Modules.Gambling.Common.AnimalRacing; | ||||
|  | ||||
| namespace NadekoBot.Modules.Gambling.Services | ||||
| { | ||||
|     public class AnimalRaceService : INService, IUnloadableService | ||||
|     { | ||||
|         public ConcurrentDictionary<ulong, AnimalRace> AnimalRaces { get; } = new ConcurrentDictionary<ulong, AnimalRace>(); | ||||
|  | ||||
|         public Task Unload() | ||||
|         { | ||||
|             foreach (var kvp in AnimalRaces) | ||||
|             { | ||||
|                 try { kvp.Value.Dispose(); } catch { } | ||||
|             } | ||||
|             return Task.CompletedTask; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,66 @@ | ||||
| using Discord; | ||||
| using Discord.Commands; | ||||
| using NadekoBot.Modules.Gambling.Common; | ||||
| using NadekoBot.Modules.Gambling.Common.CurrencyEvents; | ||||
| using NadekoBot.Core.Services; | ||||
| using System.Collections.Concurrent; | ||||
| using System.Collections.Generic; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| namespace NadekoBot.Modules.Gambling.Services | ||||
| { | ||||
|     public class CurrencyEventsService : INService, IUnloadableService | ||||
|     { | ||||
|         public ConcurrentDictionary<ulong, List<ReactionEvent>> ReactionEvents { get; } | ||||
|  | ||||
|         public SneakyEvent SneakyEvent { get; private set; } = null; | ||||
|         private SemaphoreSlim _sneakyLock = new SemaphoreSlim(1, 1); | ||||
|  | ||||
|         public CurrencyEventsService() | ||||
|         { | ||||
|             ReactionEvents = new ConcurrentDictionary<ulong, List<ReactionEvent>>(); | ||||
|         } | ||||
|  | ||||
|         public async Task<bool> StartSneakyEvent(SneakyEvent ev, IUserMessage msg, ICommandContext ctx) | ||||
|         { | ||||
|             await _sneakyLock.WaitAsync().ConfigureAwait(false); | ||||
|             try | ||||
|             { | ||||
|                 if (SneakyEvent != null) | ||||
|                     return false; | ||||
|  | ||||
|                 SneakyEvent = ev; | ||||
|                 ev.OnEnded += () => SneakyEvent = null; | ||||
|                 var _ = SneakyEvent.Start(msg, ctx).ConfigureAwait(false); | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 _sneakyLock.Release(); | ||||
|             } | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         public async Task Unload() | ||||
|         { | ||||
|             foreach (var kvp in ReactionEvents) | ||||
|             { | ||||
|                 foreach (var ev in kvp.Value) | ||||
|                 { | ||||
|                     try { await ev.Stop().ConfigureAwait(false); } catch { } | ||||
|                 } | ||||
|             } | ||||
|             ReactionEvents.Clear(); | ||||
|  | ||||
|             await _sneakyLock.WaitAsync().ConfigureAwait(false); | ||||
|             try | ||||
|             { | ||||
|                 await SneakyEvent.Stop().ConfigureAwait(false); | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 _sneakyLock.Release(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										12
									
								
								NadekoBot.Core/Modules/Gambling/Services/WaifuService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								NadekoBot.Core/Modules/Gambling/Services/WaifuService.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| using NadekoBot.Core.Services; | ||||
| using System; | ||||
| using System.Collections.Concurrent; | ||||
|  | ||||
| namespace NadekoBot.Modules.Gambling.Services | ||||
| { | ||||
|     public class WaifuService : INService | ||||
|     { | ||||
|         public ConcurrentDictionary<ulong, DateTime> DivorceCooldowns { get; } = new ConcurrentDictionary<ulong, DateTime>(); | ||||
|         public ConcurrentDictionary<ulong, DateTime> AffinityCooldowns { get; } = new ConcurrentDictionary<ulong, DateTime>(); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										239
									
								
								NadekoBot.Core/Modules/Gambling/SlotCommands.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										239
									
								
								NadekoBot.Core/Modules/Gambling/SlotCommands.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,239 @@ | ||||
| using Discord; | ||||
| using Discord.Commands; | ||||
| using ImageSharp; | ||||
| using NadekoBot.Extensions; | ||||
| using NadekoBot.Core.Services; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| using NadekoBot.Common; | ||||
| using NadekoBot.Common.Attributes; | ||||
| using SixLabors.Primitives; | ||||
|  | ||||
| namespace NadekoBot.Modules.Gambling | ||||
| { | ||||
|     public partial class Gambling | ||||
|     { | ||||
|         [Group] | ||||
|         public class SlotCommands : NadekoSubmodule | ||||
|         { | ||||
|             private static int _totalBet; | ||||
|             private static int _totalPaidOut; | ||||
|  | ||||
|             private static readonly HashSet<ulong> _runningUsers = new HashSet<ulong>(); | ||||
|             private readonly IBotConfigProvider _bc; | ||||
|  | ||||
|             private const int _alphaCutOut = byte.MaxValue / 3; | ||||
|  | ||||
|             //here is a payout chart | ||||
|             //https://lh6.googleusercontent.com/-i1hjAJy_kN4/UswKxmhrbPI/AAAAAAAAB1U/82wq_4ZZc-Y/DE6B0895-6FC1-48BE-AC4F-14D1B91AB75B.jpg | ||||
|             //thanks to judge for helping me with this | ||||
|  | ||||
|             private readonly IImagesService _images; | ||||
|             private readonly CurrencyService _cs; | ||||
|  | ||||
|             public SlotCommands(IImagesService images, IBotConfigProvider bc, CurrencyService cs) | ||||
|             { | ||||
|                 _images = images; | ||||
|                 _bc = bc; | ||||
|                 _cs = cs; | ||||
|             } | ||||
|  | ||||
|             public class SlotMachine | ||||
|             { | ||||
|                 public const int MaxValue = 5; | ||||
|  | ||||
|                 static readonly List<Func<int[], int>> _winningCombos = new List<Func<int[], int>>() | ||||
|                 { | ||||
|                     //three flowers | ||||
|                     (arr) => arr.All(a=>a==MaxValue) ? 30 : 0, | ||||
|                     //three of the same | ||||
|                     (arr) => !arr.Any(a => a != arr[0]) ? 10 : 0, | ||||
|                     //two flowers | ||||
|                     (arr) => arr.Count(a => a == MaxValue) == 2 ? 4 : 0, | ||||
|                     //one flower | ||||
|                     (arr) => arr.Any(a => a == MaxValue) ? 1 : 0, | ||||
|                 }; | ||||
|  | ||||
|                 public static SlotResult Pull() | ||||
|                 { | ||||
|                     var numbers = new int[3]; | ||||
|                     for (var i = 0; i < numbers.Length; i++) | ||||
|                     { | ||||
|                         numbers[i] = new NadekoRandom().Next(0, MaxValue + 1); | ||||
|                     } | ||||
|                     var multi = 0; | ||||
|                     foreach (var t in _winningCombos) | ||||
|                     { | ||||
|                         multi = t(numbers); | ||||
|                         if (multi != 0) | ||||
|                             break; | ||||
|                     } | ||||
|  | ||||
|                     return new SlotResult(numbers, multi); | ||||
|                 } | ||||
|  | ||||
|                 public struct SlotResult | ||||
|                 { | ||||
|                     public int[] Numbers { get; } | ||||
|                     public int Multiplier { get; } | ||||
|                     public SlotResult(int[] nums, int multi) | ||||
|                     { | ||||
|                         Numbers = nums; | ||||
|                         Multiplier = multi; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [OwnerOnly] | ||||
|             public async Task SlotStats() | ||||
|             { | ||||
|                 //i remembered to not be a moron | ||||
|                 var paid = _totalPaidOut; | ||||
|                 var bet = _totalBet; | ||||
|  | ||||
|                 if (bet <= 0) | ||||
|                     bet = 1; | ||||
|  | ||||
|                 var embed = new EmbedBuilder() | ||||
|                     .WithOkColor() | ||||
|                     .WithTitle("Slot Stats") | ||||
|                     .AddField(efb => efb.WithName("Total Bet").WithValue(bet.ToString()).WithIsInline(true)) | ||||
|                     .AddField(efb => efb.WithName("Paid Out").WithValue(paid.ToString()).WithIsInline(true)) | ||||
|                     .WithFooter(efb => efb.WithText($"Payout Rate: {paid * 1.0 / bet * 100:f4}%")); | ||||
|  | ||||
|                 await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); | ||||
|             } | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [OwnerOnly] | ||||
|             public async Task SlotTest(int tests = 1000) | ||||
|             { | ||||
|                 if (tests <= 0) | ||||
|                     return; | ||||
|                 //multi vs how many times it occured | ||||
|                 var dict = new Dictionary<int, int>(); | ||||
|                 for (int i = 0; i < tests; i++) | ||||
|                 { | ||||
|                     var res = SlotMachine.Pull(); | ||||
|                     if (dict.ContainsKey(res.Multiplier)) | ||||
|                         dict[res.Multiplier] += 1; | ||||
|                     else | ||||
|                         dict.Add(res.Multiplier, 1); | ||||
|                 } | ||||
|  | ||||
|                 var sb = new StringBuilder(); | ||||
|                 const int bet = 1; | ||||
|                 int payout = 0; | ||||
|                 foreach (var key in dict.Keys.OrderByDescending(x => x)) | ||||
|                 { | ||||
|                     sb.AppendLine($"x{key} occured {dict[key]} times. {dict[key] * 1.0f / tests * 100}%"); | ||||
|                     payout += key * dict[key]; | ||||
|                 } | ||||
|                 await Context.Channel.SendConfirmAsync("Slot Test Results", sb.ToString(), | ||||
|                     footer: $"Total Bet: {tests * bet} | Payout: {payout * bet} | {payout * 1.0f / tests * 100}%"); | ||||
|             } | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             public async Task Slot(int amount = 0) | ||||
|             { | ||||
|                 if (!_runningUsers.Add(Context.User.Id)) | ||||
|                     return; | ||||
|                 try | ||||
|                 { | ||||
|                     if (amount < 1) | ||||
|                     { | ||||
|                         await ReplyErrorLocalized("min_bet_limit", 1 + _bc.BotConfig.CurrencySign).ConfigureAwait(false); | ||||
|                         return; | ||||
|                     } | ||||
|                     const int maxAmount = 9999; | ||||
|                     if (amount > maxAmount) | ||||
|                     { | ||||
|                         GetText("slot_maxbet", maxAmount + _bc.BotConfig.CurrencySign); | ||||
|                         await ReplyErrorLocalized("max_bet_limit", maxAmount + _bc.BotConfig.CurrencySign).ConfigureAwait(false); | ||||
|                         return; | ||||
|                     } | ||||
|  | ||||
|                     if (!await _cs.RemoveAsync(Context.User, "Slot Machine", amount, false)) | ||||
|                     { | ||||
|                         await ReplyErrorLocalized("not_enough", _bc.BotConfig.CurrencySign).ConfigureAwait(false); | ||||
|                         return; | ||||
|                     } | ||||
|                     Interlocked.Add(ref _totalBet, amount); | ||||
|                     using (var bgFileStream = _images.SlotBackground.ToStream()) | ||||
|                     { | ||||
|                         var bgImage = ImageSharp.Image.Load(bgFileStream); | ||||
|  | ||||
|                         var result = SlotMachine.Pull(); | ||||
|                         int[] numbers = result.Numbers; | ||||
|  | ||||
|                         for (int i = 0; i < 3; i++) | ||||
|                         { | ||||
|                             using (var file = _images.SlotEmojis[numbers[i]].ToStream()) | ||||
|                             using (var randomImage = ImageSharp.Image.Load(file)) | ||||
|                             { | ||||
|                                 bgImage.DrawImage(randomImage, 100, default, new Point(95 + 142 * i, 330)); | ||||
|                             } | ||||
|                         } | ||||
|  | ||||
|                         var won = amount * result.Multiplier; | ||||
|                         var printWon = won; | ||||
|                         var n = 0; | ||||
|                         do | ||||
|                         { | ||||
|                             var digit = printWon % 10; | ||||
|                             using (var fs = _images.SlotNumbers[digit].ToStream()) | ||||
|                             using (var img = ImageSharp.Image.Load(fs)) | ||||
|                             { | ||||
|                                 bgImage.DrawImage(img, 100, default, new Point(230 - n * 16, 462)); | ||||
|                             } | ||||
|                             n++; | ||||
|                         } while ((printWon /= 10) != 0); | ||||
|  | ||||
|                         var printAmount = amount; | ||||
|                         n = 0; | ||||
|                         do | ||||
|                         { | ||||
|                             var digit = printAmount % 10; | ||||
|                             using (var fs = _images.SlotNumbers[digit].ToStream()) | ||||
|                             using (var img = ImageSharp.Image.Load(fs)) | ||||
|                             { | ||||
|                                 bgImage.DrawImage(img, 100, default, new Point(395 - n * 16, 462)); | ||||
|                             } | ||||
|                             n++; | ||||
|                         } while ((printAmount /= 10) != 0); | ||||
|  | ||||
|                         var msg = GetText("better_luck"); | ||||
|                         if (result.Multiplier != 0) | ||||
|                         { | ||||
|                             await _cs.AddAsync(Context.User, $"Slot Machine x{result.Multiplier}", amount * result.Multiplier, false); | ||||
|                             Interlocked.Add(ref _totalPaidOut, amount * result.Multiplier); | ||||
|                             if (result.Multiplier == 1) | ||||
|                                 msg = GetText("slot_single", _bc.BotConfig.CurrencySign, 1); | ||||
|                             else if (result.Multiplier == 4) | ||||
|                                 msg = GetText("slot_two", _bc.BotConfig.CurrencySign, 4); | ||||
|                             else if (result.Multiplier == 10) | ||||
|                                 msg = GetText("slot_three", 10); | ||||
|                             else if (result.Multiplier == 30) | ||||
|                                 msg = GetText("slot_jackpot", 30); | ||||
|                         } | ||||
|  | ||||
|                         await Context.Channel.SendFileAsync(bgImage.ToStream(), "result.png", Context.User.Mention + " " + msg + $"\n`{GetText("slot_bet")}:`{amount} `{GetText("slot_won")}:` {amount * result.Multiplier}{_bc.BotConfig.CurrencySign}").ConfigureAwait(false); | ||||
|                     } | ||||
|                 } | ||||
|                 finally | ||||
|                 { | ||||
|                     var _ = Task.Run(async () => | ||||
|                     { | ||||
|                         await Task.Delay(1500); | ||||
|                         _runningUsers.Remove(Context.User.Id); | ||||
|                     }); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										617
									
								
								NadekoBot.Core/Modules/Gambling/WaifuClaimCommands.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										617
									
								
								NadekoBot.Core/Modules/Gambling/WaifuClaimCommands.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,617 @@ | ||||
| using Discord; | ||||
| using Discord.Commands; | ||||
| using NadekoBot.Extensions; | ||||
| using NadekoBot.Core.Services; | ||||
| using NadekoBot.Core.Services.Database.Models; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Threading.Tasks; | ||||
| using NadekoBot.Common; | ||||
| using NadekoBot.Common.Attributes; | ||||
| using NadekoBot.Modules.Gambling.Services; | ||||
|  | ||||
| namespace NadekoBot.Modules.Gambling | ||||
| { | ||||
|     public partial class Gambling | ||||
|     { | ||||
|         public enum ClaimTitles | ||||
|         { | ||||
|             Lonely, | ||||
|             Devoted, | ||||
|             Rookie, | ||||
|             Schemer, | ||||
|             Dilettante, | ||||
|             Intermediate, | ||||
|             Seducer, | ||||
|             Expert, | ||||
|             Veteran, | ||||
|             Incubis, | ||||
|             Harem_King, | ||||
|             Harem_God, | ||||
|         } | ||||
|  | ||||
|         public enum AffinityTitles | ||||
|         { | ||||
|             Pure, | ||||
|             Faithful, | ||||
|             Defiled, | ||||
|             Cheater, | ||||
|             Tainted, | ||||
|             Corrupted, | ||||
|             Lewd, | ||||
|             Sloot, | ||||
|             Depraved, | ||||
|             Harlot | ||||
|         } | ||||
|  | ||||
|         [Group] | ||||
|         public class WaifuClaimCommands : NadekoSubmodule<WaifuService> | ||||
|         { | ||||
|             enum WaifuClaimResult | ||||
|             { | ||||
|                 Success, | ||||
|                 NotEnoughFunds, | ||||
|                 InsufficientAmount | ||||
|             } | ||||
|  | ||||
|             public WaifuClaimCommands(IBotConfigProvider bc, CurrencyService cs, DbService db) | ||||
|             { | ||||
|                 _bc = bc; | ||||
|                 _cs = cs; | ||||
|                 _db = db; | ||||
|             } | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             public async Task WaifuClaim(int amount, [Remainder]IUser target) | ||||
|             { | ||||
|                 if (amount < 50) | ||||
|                 { | ||||
|                     await ReplyErrorLocalized("waifu_isnt_cheap", 50 + _bc.BotConfig.CurrencySign).ConfigureAwait(false); | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 if (target.Id == Context.User.Id) | ||||
|                 { | ||||
|                     await ReplyErrorLocalized("waifu_not_yourself").ConfigureAwait(false); | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 WaifuClaimResult result; | ||||
|                 WaifuInfo w; | ||||
|                 bool isAffinity; | ||||
|                 using (var uow = _db.UnitOfWork) | ||||
|                 { | ||||
|                     w = uow.Waifus.ByWaifuUserId(target.Id); | ||||
|                     isAffinity = (w?.Affinity?.UserId == Context.User.Id); | ||||
|                     if (w == null) | ||||
|                     { | ||||
|                         var claimer = uow.DiscordUsers.GetOrCreate(Context.User); | ||||
|                         var waifu = uow.DiscordUsers.GetOrCreate(target); | ||||
|                         if (!await _cs.RemoveAsync(Context.User.Id, "Claimed Waifu", amount, uow).ConfigureAwait(false)) | ||||
|                         { | ||||
|                             result = WaifuClaimResult.NotEnoughFunds; | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             uow.Waifus.Add(w = new WaifuInfo() | ||||
|                             { | ||||
|                                 Waifu = waifu, | ||||
|                                 Claimer = claimer, | ||||
|                                 Affinity = null, | ||||
|                                 Price = amount | ||||
|                             }); | ||||
|                             uow._context.WaifuUpdates.Add(new WaifuUpdate() | ||||
|                             { | ||||
|                                 User = waifu, | ||||
|                                 Old = null, | ||||
|                                 New = claimer, | ||||
|                                 UpdateType = WaifuUpdateType.Claimed | ||||
|                             }); | ||||
|                             result = WaifuClaimResult.Success; | ||||
|                         } | ||||
|                     } | ||||
|                     else if (isAffinity && amount > w.Price * 0.88f) | ||||
|                     { | ||||
|                         if (!await _cs.RemoveAsync(Context.User.Id, "Claimed Waifu", amount, uow).ConfigureAwait(false)) | ||||
|                         { | ||||
|                             result = WaifuClaimResult.NotEnoughFunds; | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             var oldClaimer = w.Claimer; | ||||
|                             w.Claimer = uow.DiscordUsers.GetOrCreate(Context.User); | ||||
|                             w.Price = amount + (amount / 4); | ||||
|                             result = WaifuClaimResult.Success; | ||||
|  | ||||
|                             uow._context.WaifuUpdates.Add(new WaifuUpdate() | ||||
|                             { | ||||
|                                 User = w.Waifu, | ||||
|                                 Old = oldClaimer, | ||||
|                                 New = w.Claimer, | ||||
|                                 UpdateType = WaifuUpdateType.Claimed | ||||
|                             }); | ||||
|                         } | ||||
|                     } | ||||
|                     else if (amount >= w.Price * 1.1f) // if no affinity | ||||
|                     { | ||||
|                         if (!await _cs.RemoveAsync(Context.User.Id, "Claimed Waifu", amount, uow).ConfigureAwait(false)) | ||||
|                         { | ||||
|                             result = WaifuClaimResult.NotEnoughFunds; | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             var oldClaimer = w.Claimer; | ||||
|                             w.Claimer = uow.DiscordUsers.GetOrCreate(Context.User); | ||||
|                             w.Price = amount; | ||||
|                             result = WaifuClaimResult.Success; | ||||
|  | ||||
|                             uow._context.WaifuUpdates.Add(new WaifuUpdate() | ||||
|                             { | ||||
|                                 User = w.Waifu, | ||||
|                                 Old = oldClaimer, | ||||
|                                 New = w.Claimer, | ||||
|                                 UpdateType = WaifuUpdateType.Claimed | ||||
|                             }); | ||||
|                         } | ||||
|                     } | ||||
|                     else | ||||
|                         result = WaifuClaimResult.InsufficientAmount; | ||||
|  | ||||
|  | ||||
|                     await uow.CompleteAsync().ConfigureAwait(false); | ||||
|                 } | ||||
|  | ||||
|                 if (result == WaifuClaimResult.InsufficientAmount) | ||||
|                 { | ||||
|                     await ReplyErrorLocalized("waifu_not_enough", Math.Ceiling(w.Price * (isAffinity ? 0.88f : 1.1f))).ConfigureAwait(false); | ||||
|                     return; | ||||
|                 } | ||||
|                 if (result == WaifuClaimResult.NotEnoughFunds) | ||||
|                 { | ||||
|                     await ReplyErrorLocalized("not_enough", _bc.BotConfig.CurrencySign).ConfigureAwait(false); | ||||
|                     return; | ||||
|                 } | ||||
|                 var msg = GetText("waifu_claimed",  | ||||
|                     Format.Bold(target.ToString()),  | ||||
|                     amount + _bc.BotConfig.CurrencySign); | ||||
|                 if (w.Affinity?.UserId == Context.User.Id) | ||||
|                     msg += "\n" + GetText("waifu_fulfilled", target, w.Price + _bc.BotConfig.CurrencySign); | ||||
|                 else | ||||
|                     msg = " " + msg; | ||||
|                 await Context.Channel.SendConfirmAsync(Context.User.Mention + msg).ConfigureAwait(false); | ||||
|             } | ||||
|  | ||||
|             public enum DivorceResult | ||||
|             { | ||||
|                 Success, | ||||
|                 SucessWithPenalty, | ||||
|                 NotYourWife, | ||||
|                 Cooldown | ||||
|             } | ||||
|  | ||||
|  | ||||
|             private static readonly TimeSpan _divorceLimit = TimeSpan.FromHours(6); | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             [Priority(0)] | ||||
|             public Task Divorce([Remainder]IGuildUser target) => Divorce(target.Id); | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             [Priority(1)] | ||||
|             public async Task Divorce([Remainder]ulong targetId) | ||||
|             { | ||||
|                 if (targetId == Context.User.Id) | ||||
|                     return; | ||||
|  | ||||
|                 DivorceResult result; | ||||
|                 var difference = TimeSpan.Zero; | ||||
|                 var amount = 0; | ||||
|                 WaifuInfo w = null; | ||||
|                 using (var uow = _db.UnitOfWork) | ||||
|                 { | ||||
|                     w = uow.Waifus.ByWaifuUserId(targetId); | ||||
|                     var now = DateTime.UtcNow; | ||||
|                     if (w?.Claimer == null || w.Claimer.UserId != Context.User.Id) | ||||
|                         result = DivorceResult.NotYourWife; | ||||
|                     else if (_service.DivorceCooldowns.AddOrUpdate(Context.User.Id, | ||||
|                         now, | ||||
|                         (key, old) => ((difference = now.Subtract(old)) > _divorceLimit) ? now : old) != now) | ||||
|                     { | ||||
|                         result = DivorceResult.Cooldown; | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         amount = w.Price / 2; | ||||
|  | ||||
|                         if (w.Affinity?.UserId == Context.User.Id) | ||||
|                         { | ||||
|                             await _cs.AddAsync(w.Waifu.UserId, "Waifu Compensation", amount, uow).ConfigureAwait(false); | ||||
|                             w.Price = (int)Math.Floor(w.Price * 0.75f); | ||||
|                             result = DivorceResult.SucessWithPenalty; | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             await _cs.AddAsync(Context.User.Id, "Waifu Refund", amount, uow).ConfigureAwait(false); | ||||
|  | ||||
|                             result = DivorceResult.Success; | ||||
|                         } | ||||
|                         var oldClaimer = w.Claimer; | ||||
|                         w.Claimer = null; | ||||
|  | ||||
|                         uow._context.WaifuUpdates.Add(new WaifuUpdate() | ||||
|                         { | ||||
|                             User = w.Waifu, | ||||
|                             Old = oldClaimer, | ||||
|                             New = null, | ||||
|                             UpdateType = WaifuUpdateType.Claimed | ||||
|                         }); | ||||
|                     } | ||||
|  | ||||
|                     await uow.CompleteAsync().ConfigureAwait(false); | ||||
|                 } | ||||
|  | ||||
|                 if (result == DivorceResult.SucessWithPenalty) | ||||
|                 { | ||||
|                     await ReplyConfirmLocalized("waifu_divorced_like", Format.Bold(w.Waifu.ToString()), amount + _bc.BotConfig.CurrencySign).ConfigureAwait(false); | ||||
|                 } | ||||
|                 else if (result == DivorceResult.Success) | ||||
|                 { | ||||
|                     await ReplyConfirmLocalized("waifu_divorced_notlike", amount + _bc.BotConfig.CurrencySign).ConfigureAwait(false); | ||||
|                 } | ||||
|                 else if (result == DivorceResult.NotYourWife) | ||||
|                 { | ||||
|                     await ReplyErrorLocalized("waifu_not_yours").ConfigureAwait(false); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     var remaining = _divorceLimit.Subtract(difference); | ||||
|                     await ReplyErrorLocalized("waifu_recent_divorce",  | ||||
|                         Format.Bold(((int)remaining.TotalHours).ToString()), | ||||
|                         Format.Bold(remaining.Minutes.ToString())).ConfigureAwait(false); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             private static readonly TimeSpan _affinityLimit = TimeSpan.FromMinutes(30); | ||||
|             private readonly IBotConfigProvider _bc; | ||||
|             private readonly CurrencyService _cs; | ||||
|             private readonly DbService _db; | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             public async Task WaifuClaimerAffinity([Remainder]IGuildUser u = null) | ||||
|             { | ||||
|                 if (u?.Id == Context.User.Id) | ||||
|                 { | ||||
|                     await ReplyErrorLocalized("waifu_egomaniac").ConfigureAwait(false); | ||||
|                     return; | ||||
|                 } | ||||
|                 DiscordUser oldAff = null; | ||||
|                 var sucess = false; | ||||
|                 var cooldown = false; | ||||
|                 var difference = TimeSpan.Zero; | ||||
|                 using (var uow = _db.UnitOfWork) | ||||
|                 { | ||||
|                     var w = uow.Waifus.ByWaifuUserId(Context.User.Id); | ||||
|                     var newAff = u == null ? null : uow.DiscordUsers.GetOrCreate(u); | ||||
|                     var now = DateTime.UtcNow; | ||||
|                     if (w?.Affinity?.UserId == u?.Id) | ||||
|                     { | ||||
|                     } | ||||
|                     else if (_service.AffinityCooldowns.AddOrUpdate(Context.User.Id, | ||||
|                         now, | ||||
|                         (key, old) => ((difference = now.Subtract(old)) > _affinityLimit) ? now : old) != now) | ||||
|                     { | ||||
|                         cooldown = true; | ||||
|                     } | ||||
|                     else if (w == null) | ||||
|                     { | ||||
|                         var thisUser = uow.DiscordUsers.GetOrCreate(Context.User); | ||||
|                         uow.Waifus.Add(new WaifuInfo() | ||||
|                         { | ||||
|                             Affinity = newAff, | ||||
|                             Waifu = thisUser, | ||||
|                             Price = 1, | ||||
|                             Claimer = null | ||||
|                         }); | ||||
|                         sucess = true; | ||||
|  | ||||
|                         uow._context.WaifuUpdates.Add(new WaifuUpdate() | ||||
|                         { | ||||
|                             User = thisUser, | ||||
|                             Old = null, | ||||
|                             New = newAff, | ||||
|                             UpdateType = WaifuUpdateType.AffinityChanged | ||||
|                         }); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         if (w.Affinity != null) | ||||
|                             oldAff = w.Affinity; | ||||
|                         w.Affinity = newAff; | ||||
|                         sucess = true; | ||||
|  | ||||
|                         uow._context.WaifuUpdates.Add(new WaifuUpdate() | ||||
|                         { | ||||
|                             User = w.Waifu, | ||||
|                             Old = oldAff, | ||||
|                             New = newAff, | ||||
|                             UpdateType = WaifuUpdateType.AffinityChanged | ||||
|                         }); | ||||
|                     } | ||||
|  | ||||
|                     await uow.CompleteAsync().ConfigureAwait(false); | ||||
|                 } | ||||
|                 if (!sucess) | ||||
|                 { | ||||
|                     if (cooldown) | ||||
|                     { | ||||
|                         var remaining = _affinityLimit.Subtract(difference); | ||||
|                         await ReplyErrorLocalized("waifu_affinity_cooldown",  | ||||
|                             Format.Bold(((int)remaining.TotalHours).ToString()), | ||||
|                             Format.Bold(remaining.Minutes.ToString())).ConfigureAwait(false); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         await ReplyErrorLocalized("waifu_affinity_already").ConfigureAwait(false); | ||||
|                     } | ||||
|                     return; | ||||
|                 } | ||||
|                 if (u == null) | ||||
|                 { | ||||
|                     await ReplyConfirmLocalized("waifu_affinity_reset").ConfigureAwait(false); | ||||
|                 } | ||||
|                 else if (oldAff == null) | ||||
|                 { | ||||
|                     await ReplyConfirmLocalized("waifu_affinity_set", Format.Bold(u.ToString())).ConfigureAwait(false); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     await ReplyConfirmLocalized("waifu_affinity_changed", Format.Bold(oldAff.ToString()), Format.Bold(u.ToString())).ConfigureAwait(false); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             public async Task WaifuLeaderboard(int page = 1) | ||||
|             { | ||||
|                 page--; | ||||
|  | ||||
|                 if (page < 0) | ||||
|                     return; | ||||
|  | ||||
|                 IList<WaifuInfo> waifus; | ||||
|                 using (var uow = _db.UnitOfWork) | ||||
|                 { | ||||
|                     waifus = uow.Waifus.GetTop(9, page * 9); | ||||
|                 } | ||||
|  | ||||
|                 if (waifus.Count == 0) | ||||
|                 { | ||||
|                     await ReplyConfirmLocalized("waifus_none").ConfigureAwait(false); | ||||
|                     return; | ||||
|                 } | ||||
|                  | ||||
|                 var embed = new EmbedBuilder() | ||||
|                     .WithTitle(GetText("waifus_top_waifus")) | ||||
|                     .WithOkColor(); | ||||
|  | ||||
|                 for (var i = 0; i < waifus.Count; i++) | ||||
|                 { | ||||
|                     var w = waifus[i]; | ||||
|  | ||||
|                     var j = i; | ||||
|                     embed.AddField(efb => efb.WithName("#" + ((page * 9) + j + 1) + " - " + w.Price + _bc.BotConfig.CurrencySign).WithValue(w.ToString()).WithIsInline(false)); | ||||
|                 } | ||||
|  | ||||
|                 await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); | ||||
|             } | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             public async Task WaifuInfo([Remainder]IGuildUser target = null) | ||||
|             { | ||||
|                 if (target == null) | ||||
|                     target = (IGuildUser)Context.User; | ||||
|                 WaifuInfo w; | ||||
|                 IList<WaifuInfo> claims; | ||||
|                 int divorces; | ||||
|                 using (var uow = _db.UnitOfWork) | ||||
|                 { | ||||
|                     w = uow.Waifus.ByWaifuUserId(target.Id); | ||||
|                     claims = uow.Waifus.ByClaimerUserId(target.Id); | ||||
|                     divorces = uow._context.WaifuUpdates.Count(x => x.Old != null && | ||||
|                         x.Old.UserId == target.Id && | ||||
|                         x.UpdateType == WaifuUpdateType.Claimed && | ||||
|                         x.New == null); | ||||
|                     if (w == null) | ||||
|                     { | ||||
|                         uow.Waifus.Add(w = new WaifuInfo() | ||||
|                         { | ||||
|                             Affinity = null, | ||||
|                             Claimer = null, | ||||
|                             Price = 1, | ||||
|                             Waifu = uow.DiscordUsers.GetOrCreate(target), | ||||
|                         }); | ||||
|                     } | ||||
|  | ||||
|                     w.Waifu.Username = target.Username; | ||||
|                     w.Waifu.Discriminator = target.Discriminator; | ||||
|                     await uow.CompleteAsync().ConfigureAwait(false); | ||||
|                 } | ||||
|  | ||||
|                 var claimInfo = GetClaimTitle(target.Id); | ||||
|                 var affInfo = GetAffinityTitle(target.Id); | ||||
|  | ||||
|                 var rng = new NadekoRandom(); | ||||
|  | ||||
|                 var nobody = GetText("nobody"); | ||||
|                 var embed = new EmbedBuilder() | ||||
|                     .WithOkColor() | ||||
|                     .WithTitle("Waifu " + w.Waifu + " - \"the " + claimInfo.Title + "\"") | ||||
|                     .AddField(efb => efb.WithName(GetText("price")).WithValue(w.Price.ToString()).WithIsInline(true)) | ||||
|                     .AddField(efb => efb.WithName(GetText("claimed_by")).WithValue(w.Claimer?.ToString() ?? nobody).WithIsInline(true)) | ||||
|                     .AddField(efb => efb.WithName(GetText("likes")).WithValue(w.Affinity?.ToString() ?? nobody).WithIsInline(true)) | ||||
|                     .AddField(efb => efb.WithName(GetText("changes_of_heart")).WithValue($"{affInfo.Count} - \"the {affInfo.Title}\"").WithIsInline(true)) | ||||
|                     .AddField(efb => efb.WithName(GetText("divorces")).WithValue(divorces.ToString()).WithIsInline(true)) | ||||
|                     .AddField(efb => efb.WithName(GetText("gifts")).WithValue(!w.Items.Any() ? "-" : string.Join("\n", w.Items.OrderBy(x => x.Price).GroupBy(x => x.ItemEmoji).Select(x => $"{x.Key} x{x.Count()}"))).WithIsInline(false)) | ||||
|                     .AddField(efb => efb.WithName($"Waifus ({claims.Count})").WithValue(claims.Count == 0 ? nobody : string.Join("\n", claims.OrderBy(x => rng.Next()).Take(30).Select(x => x.Waifu))).WithIsInline(false)); | ||||
|  | ||||
|                 await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); | ||||
|             } | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             [Priority(1)] | ||||
|             public async Task WaifuGift() | ||||
|             { | ||||
|                 var embed = new EmbedBuilder() | ||||
|                     .WithTitle(GetText("waifu_gift_shop")) | ||||
|                     .WithOkColor(); | ||||
|  | ||||
|                 Enum.GetValues(typeof(WaifuItem.ItemName)) | ||||
|                     .Cast<WaifuItem.ItemName>() | ||||
|                     .Select(x => WaifuItem.GetItem(x)) | ||||
|                     .ForEach(x => embed.AddField(f => f.WithName(x.ItemEmoji + " " + x.Item).WithValue(x.Price).WithIsInline(true))); | ||||
|  | ||||
|                     await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); | ||||
|             } | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             [Priority(0)] | ||||
|             public async Task WaifuGift(WaifuItem.ItemName item, [Remainder] IUser waifu) | ||||
|             { | ||||
|                 if (waifu.Id == Context.User.Id) | ||||
|                     return; | ||||
|  | ||||
|                 var itemObj = WaifuItem.GetItem(item); | ||||
|  | ||||
|                 using (var uow = _db.UnitOfWork) | ||||
|                 { | ||||
|                     var w = uow.Waifus.ByWaifuUserId(waifu.Id); | ||||
|  | ||||
|                     //try to buy the item first | ||||
|  | ||||
|                     if (!await _cs.RemoveAsync(Context.User.Id, "Bought waifu item", itemObj.Price, uow)) | ||||
|                     { | ||||
|                         await ReplyErrorLocalized("not_enough", _bc.BotConfig.CurrencySign).ConfigureAwait(false); | ||||
|                         return; | ||||
|                     } | ||||
|                     if (w == null) | ||||
|                     { | ||||
|                         uow.Waifus.Add(w = new WaifuInfo() | ||||
|                         { | ||||
|                             Affinity = null, | ||||
|                             Claimer = null, | ||||
|                             Price = 1, | ||||
|                             Waifu = uow.DiscordUsers.GetOrCreate(waifu), | ||||
|                         }); | ||||
|  | ||||
|                         w.Waifu.Username = waifu.Username; | ||||
|                         w.Waifu.Discriminator = waifu.Discriminator; | ||||
|                     } | ||||
|                     w.Items.Add(itemObj); | ||||
|                     if (w.Claimer?.UserId == Context.User.Id) | ||||
|                     { | ||||
|                         w.Price += itemObj.Price; | ||||
|                     } | ||||
|                     else | ||||
|                         w.Price += itemObj.Price / 2; | ||||
|  | ||||
|                     await uow.CompleteAsync().ConfigureAwait(false); | ||||
|                 } | ||||
|  | ||||
|                 await ReplyConfirmLocalized("waifu_gift", Format.Bold(item.ToString() + " " +itemObj.ItemEmoji), Format.Bold(waifu.ToString())).ConfigureAwait(false); | ||||
|             } | ||||
|  | ||||
|  | ||||
|             public struct WaifuProfileTitle | ||||
|             { | ||||
|                 public int Count { get; } | ||||
|                 public string Title { get; } | ||||
|  | ||||
|                 public WaifuProfileTitle(int count, string title) | ||||
|                 { | ||||
|                     Count = count; | ||||
|                     Title = title; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             private WaifuProfileTitle GetClaimTitle(ulong userId) | ||||
|             { | ||||
|                 int count; | ||||
|                 using (var uow = _db.UnitOfWork) | ||||
|                 { | ||||
|                     count = uow.Waifus.ByClaimerUserId(userId).Count; | ||||
|                 } | ||||
|  | ||||
|                 ClaimTitles title; | ||||
|                 if (count == 0) | ||||
|                     title = ClaimTitles.Lonely; | ||||
|                 else if (count == 1) | ||||
|                     title = ClaimTitles.Devoted; | ||||
|                 else if (count < 4) | ||||
|                     title = ClaimTitles.Rookie; | ||||
|                 else if (count < 6) | ||||
|                     title = ClaimTitles.Schemer; | ||||
|                 else if (count < 8) | ||||
|                     title = ClaimTitles.Dilettante; | ||||
|                 else if (count < 10) | ||||
|                     title = ClaimTitles.Intermediate; | ||||
|                 else if (count < 12) | ||||
|                     title = ClaimTitles.Seducer; | ||||
|                 else if (count < 15) | ||||
|                     title = ClaimTitles.Expert; | ||||
|                 else if (count < 17) | ||||
|                     title = ClaimTitles.Veteran; | ||||
|                 else if (count < 25) | ||||
|                     title = ClaimTitles.Incubis; | ||||
|                 else if (count < 50) | ||||
|                     title = ClaimTitles.Harem_King; | ||||
|                 else | ||||
|                     title = ClaimTitles.Harem_God; | ||||
|  | ||||
|                 return new WaifuProfileTitle(count, title.ToString().Replace('_', ' ')); | ||||
|             } | ||||
|  | ||||
|             private WaifuProfileTitle GetAffinityTitle(ulong userId) | ||||
|             { | ||||
|                 int count; | ||||
|                 using (var uow = _db.UnitOfWork) | ||||
|                 { | ||||
|                     count = uow._context.WaifuUpdates | ||||
|                         .Where(w => w.User.UserId == userId && w.UpdateType == WaifuUpdateType.AffinityChanged && w.New != null) | ||||
|                         .GroupBy(x => x.New) | ||||
|                         .Count(); | ||||
|                 } | ||||
|  | ||||
|                 AffinityTitles title; | ||||
|                 if (count < 1) | ||||
|                     title = AffinityTitles.Pure; | ||||
|                 else if (count < 2) | ||||
|                     title = AffinityTitles.Faithful; | ||||
|                 else if (count < 4) | ||||
|                     title = AffinityTitles.Defiled; | ||||
|                 else if (count < 7) | ||||
|                     title = AffinityTitles.Cheater; | ||||
|                 else if (count < 9) | ||||
|                     title = AffinityTitles.Tainted; | ||||
|                 else if (count < 11) | ||||
|                     title = AffinityTitles.Corrupted; | ||||
|                 else if (count < 13) | ||||
|                     title = AffinityTitles.Lewd; | ||||
|                 else if (count < 15) | ||||
|                     title = AffinityTitles.Sloot; | ||||
|                 else if (count < 17) | ||||
|                     title = AffinityTitles.Depraved; | ||||
|                 else | ||||
|                     title = AffinityTitles.Harlot; | ||||
|  | ||||
|                 return new WaifuProfileTitle(count, title.ToString().Replace('_', ' ')); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										81
									
								
								NadekoBot.Core/Modules/Gambling/WheelOfFortuneCommands.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								NadekoBot.Core/Modules/Gambling/WheelOfFortuneCommands.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | ||||
| using Discord; | ||||
| using Discord.Commands; | ||||
| using NadekoBot.Common.Attributes; | ||||
| using NadekoBot.Extensions; | ||||
| using NadekoBot.Modules.Gambling.Common.WheelOfFortune; | ||||
| using NadekoBot.Core.Services; | ||||
| using System.Threading.Tasks; | ||||
| using Wof = NadekoBot.Modules.Gambling.Common.WheelOfFortune.WheelOfFortune; | ||||
|  | ||||
| namespace NadekoBot.Modules.Gambling | ||||
| { | ||||
|     public partial class Gambling | ||||
|     { | ||||
|         public class WheelOfFortuneCommands : NadekoSubmodule | ||||
|         { | ||||
|             private readonly CurrencyService _cs; | ||||
|             private readonly IBotConfigProvider _bc; | ||||
|  | ||||
|             public WheelOfFortuneCommands(CurrencyService cs, IBotConfigProvider bc) | ||||
|             { | ||||
|                 _cs = cs; | ||||
|                 _bc = bc; | ||||
|             } | ||||
|  | ||||
|             [NadekoCommand, Usage, Description, Aliases] | ||||
|             [RequireContext(ContextType.Guild)] | ||||
|             public async Task WheelOfFortune(int bet) | ||||
|             { | ||||
|                 const int minBet = 10; | ||||
|                 if (bet < minBet) | ||||
|                 { | ||||
|                     await ReplyErrorLocalized("min_bet_limit", minBet + _bc.BotConfig.CurrencySign).ConfigureAwait(false); | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 if (!await _cs.RemoveAsync(Context.User.Id, "Wheel Of Fortune - bet", bet).ConfigureAwait(false)) | ||||
|                 { | ||||
|                     await ReplyErrorLocalized("not_enough", _bc.BotConfig.CurrencySign).ConfigureAwait(false); | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 var wof = new WheelOfFortune(); | ||||
|  | ||||
|                 var amount = (int)(bet * wof.Multiplier); | ||||
|  | ||||
|                 if (amount > 0) | ||||
|                     await _cs.AddAsync(Context.User.Id, "Wheel Of Fortune - won", amount).ConfigureAwait(false); | ||||
|  | ||||
|                 await Context.Channel.SendConfirmAsync( | ||||
| Format.Bold($@"{Context.User.ToString()} won: {amount + _bc.BotConfig.CurrencySign} | ||||
|  | ||||
|    『{Wof.Multipliers[1]}』   『{Wof.Multipliers[0]}』   『{Wof.Multipliers[7]}』 | ||||
|  | ||||
| 『{Wof.Multipliers[2]}』      {wof.Emoji}      『{Wof.Multipliers[6]}』 | ||||
|  | ||||
|      『{Wof.Multipliers[3]}』   『{Wof.Multipliers[4]}』   『{Wof.Multipliers[5]}』")).ConfigureAwait(false); | ||||
|             } | ||||
|  | ||||
|             //[NadekoCommand, Usage, Description, Aliases] | ||||
|             //[RequireContext(ContextType.Guild)] | ||||
|             //public async Task WofTest(int length = 1000) | ||||
|             //{ | ||||
|             //    var mults = new Dictionary<float, int>(); | ||||
|             //    for (int i = 0; i < length; i++) | ||||
|             //    { | ||||
|             //        var x = new Wof(); | ||||
|             //        if (mults.ContainsKey(x.Multiplier)) | ||||
|             //            ++mults[x.Multiplier]; | ||||
|             //        else | ||||
|             //            mults.Add(x.Multiplier, 1); | ||||
|             //    } | ||||
|  | ||||
|             //    var payout = mults.Sum(x => x.Key * x.Value); | ||||
|             //    await Context.Channel.SendMessageAsync($"Total bet: {length}\n" + | ||||
|             //        $"Paid out: {payout}\n" + | ||||
|             //        $"Total Payout: {payout / length:F3}x") | ||||
|             //        .ConfigureAwait(false); | ||||
|             //} | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user