From a7618b1ee47c92474757a5d07b879dc10996311c Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 25 Jul 2016 04:21:37 +0200 Subject: [PATCH] Animal Racing added ($race, $joinrace). Need to add betting to it. --- NadekoBot/Classes/Extensions.cs | 3 +- NadekoBot/Classes/NadekoStats.cs | 3 +- .../Modules/Gambling/Commands/AnimalRacing.cs | 253 ++++++++++++++++++ NadekoBot/Modules/Gambling/GamblingModule.cs | 2 + NadekoBot/NadekoBot.csproj | 2 + NadekoBot/_Models/JSONModels/Configuration.cs | 11 + NadekoBot/bin/Debug/data/config_example.json | 11 +- 7 files changed, 282 insertions(+), 3 deletions(-) create mode 100644 NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs diff --git a/NadekoBot/Classes/Extensions.cs b/NadekoBot/Classes/Extensions.cs index 36ba4dec..098e4732 100644 --- a/NadekoBot/Classes/Extensions.cs +++ b/NadekoBot/Classes/Extensions.cs @@ -138,7 +138,7 @@ namespace NadekoBot.Extensions /// /// /// - public static void Shuffle(this IList list) + public static IList Shuffle(this IList list) { // Thanks to @Joe4Evr for finding a bug in the old version of the shuffle @@ -160,6 +160,7 @@ namespace NadekoBot.Extensions list[k] = list[n]; list[n] = value; } + return list; } /// diff --git a/NadekoBot/Classes/NadekoStats.cs b/NadekoBot/Classes/NadekoStats.cs index 66ef0984..7e76cd14 100644 --- a/NadekoBot/Classes/NadekoStats.cs +++ b/NadekoBot/Classes/NadekoStats.cs @@ -41,8 +41,9 @@ namespace NadekoBot var commandService = NadekoBot.Client.GetService(); statsStopwatch.Start(); +#if !NADEKO_RELEASE commandService.CommandExecuted += StatsCollector_RanCommand; - +#endif Task.Run(StartCollecting); commandLogTimer.Start(); diff --git a/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs b/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs new file mode 100644 index 00000000..de1de823 --- /dev/null +++ b/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs @@ -0,0 +1,253 @@ +ο»Ώusing NadekoBot.Classes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Discord.Commands; +using System.Collections.Concurrent; +using Discord; +using NadekoBot.Extensions; +using System.Threading; + +namespace NadekoBot.Modules.Gambling.Commands +{ + class AnimalRacing : DiscordCommand + { + public static ConcurrentDictionary AnimalRaces = new ConcurrentDictionary(); + + public AnimalRacing(DiscordModule module) : base(module) + { + } + + internal override void Init(CommandGroupBuilder cgb) + { + cgb.CreateCommand(Prefix + "race") + .Description("Starts a new animal race.") + .Do(e => { + var ar = new AnimalRace(e.Server.Id, e.Channel); + if (ar.Fail) + { + return; + } + }); + + + cgb.CreateCommand(Prefix + "joinrace") + .Alias(Prefix + "jr") + .Description("Joins a new race") + .Do(async e => { + AnimalRace ar; + + if (!AnimalRaces.TryGetValue(e.Server.Id, out ar)) { + await e.Channel.SendMessage("No race exists on this server"); + } + await ar.JoinRace(e.User); + + }); + } + + public class AnimalRace + { + + private ConcurrentQueue animals = new ConcurrentQueue(NadekoBot.Config.RaceAnimals.Shuffle()); + + public bool Fail { get; internal set; } + + public List participants = new List(); + private ulong serverId; + private int messagesSinceGameStarted = 0; + + public Channel raceChannel { get; set; } + public bool Started { get; private set; } = false; + + public AnimalRace(ulong serverId, Channel ch) + { + this.serverId = serverId; + this.raceChannel = ch; + if (!AnimalRaces.TryAdd(serverId, this)) + { + Fail = true; + return; + } + var cancelSource = new CancellationTokenSource(); + var token = cancelSource.Token; + var fullgame = CheckForFullGameAsync(token); + Task.Run(async () => + { + await raceChannel.SendMessage($"🏁`Race is starting in 20 seconds or when the room is full. Type $jr to join the race.`"); + var t = await Task.WhenAny(Task.Delay(20000, token), fullgame); + Started = true; + cancelSource.Cancel(); + if (t == fullgame) + { + await raceChannel.SendMessage("🏁`Race full, starting right now!`"); + } + else if (participants.Count > 1) { + await raceChannel.SendMessage("🏁`Game starting with " + participants.Count + " praticipants.`"); + } + else { + await raceChannel.SendMessage("🏁`Race failed to start since there was not enough participants.`"); + End(); + return; + } + await Task.Run(StartRace); + End(); + }); + } + + private void End() + { + AnimalRace throwaway; + AnimalRaces.TryRemove(serverId, out throwaway); + } + + private async Task StartRace() { + var rng = new Random(); + Participant winner = null; + Message msg = null; + int place = 1; + try + { + NadekoBot.Client.MessageReceived += Client_MessageReceived; + + while (!participants.All(p => p.Total >= 60)) + { + //update the state + participants.ForEach(p => + { + + p.Total += 1 + rng.Next(0, 10); + if (p.Total > 60) + { + p.Total = 60; + if (winner == null) + { + winner = p; + } + if (p.Place == 0) + p.Place = place++; + } + }); + + + //draw the state + + var text = $@"|πŸπŸπŸπŸπŸπŸπŸπŸπŸπŸπŸπŸπŸπŸπŸπŸ”š| +{String.Join("\n", participants.Select(p => $"{(int)(p.Total / 60f * 100),-2}%|{p.ToString()}"))} +|πŸπŸπŸπŸπŸπŸπŸπŸπŸπŸπŸπŸπŸπŸπŸπŸ”š|"; + if (msg == null || messagesSinceGameStarted >= 10) // also resend the message if channel was spammed + { + if(msg != null) + try { await msg.Delete(); } catch { } + msg = await raceChannel.SendMessage(text); + messagesSinceGameStarted = 0; + } + else + await msg.Edit(text); + + await Task.Delay(2500); + } + } + finally + { + NadekoBot.Client.MessageReceived -= Client_MessageReceived; + } + await raceChannel.SendMessage($"🏁 {winner.User.Mention} as {winner.Animal} `Won the race!`"); + + } + + private void Client_MessageReceived(object sender, MessageEventArgs e) + { + if (e.Message.IsAuthor) + return; + Console.WriteLine("Message received " + messagesSinceGameStarted); + messagesSinceGameStarted++; + } + + private async Task CheckForFullGameAsync(CancellationToken cancelToken) { + while (animals.Count > 0) + { + await Task.Delay(100,cancelToken); + } + } + + public async Task JoinRace(User u) + { + var animal = ""; + if (!animals.TryDequeue(out animal)) + { + await raceChannel.SendMessage($"{u.Mention} `There is no running race on this server.`"); + return false; + } + var p = new Participant(u, animal); + if (participants.Contains(p)) + { + await raceChannel.SendMessage($"{u.Mention} `You already joined this race.`"); + return false; + } + if (Started) + { + await raceChannel.SendMessage($"{u.Mention} `Race is already started`"); + return false; + } + participants.Add(p); + await raceChannel.SendMessage($"{u.Mention} **joined the race as a {p.Animal}**"); + return true; + } + } + + public class Participant + { + public User User { get; set; } + public string Animal { get; set; } + + public float Coeff { get; set; } + public int Total { get; set; } + + public int Place { get; set; } = 0; + + public Participant(User u, string a) + { + this.User = u; + this.Animal = a; + } + + public override int GetHashCode() + { + return User.GetHashCode(); + } + + public override bool Equals(object obj) + { + var p = obj as Participant; + return p == null? + false: + p.User == User; + } + + public override string ToString() + { + var str = new string('β€£', Total) + Animal; + if (Place == 0) + return str; + if (Place == 1) + { + return str + "πŸ†"; + } + else if (Place == 2) + { + return str + "`2nd`"; + } + else if (Place == 3) + { + return str + "`3rd`"; + } + else { + return str + $"`{Place}th`"; + } + + } + } + } +} diff --git a/NadekoBot/Modules/Gambling/GamblingModule.cs b/NadekoBot/Modules/Gambling/GamblingModule.cs index 579e1ed6..a65fe779 100644 --- a/NadekoBot/Modules/Gambling/GamblingModule.cs +++ b/NadekoBot/Modules/Gambling/GamblingModule.cs @@ -4,6 +4,7 @@ using Discord.Modules; using NadekoBot.Classes; using NadekoBot.DataModels; using NadekoBot.Extensions; +using NadekoBot.Modules.Gambling.Commands; using NadekoBot.Modules.Permissions.Classes; using System; using System.Linq; @@ -18,6 +19,7 @@ namespace NadekoBot.Modules.Gambling commands.Add(new DrawCommand(this)); commands.Add(new FlipCoinCommand(this)); commands.Add(new DiceRollCommand(this)); + commands.Add(new AnimalRacing(this)); } public override string Prefix { get; } = NadekoBot.Config.CommandPrefixes.Gambling; diff --git a/NadekoBot/NadekoBot.csproj b/NadekoBot/NadekoBot.csproj index 442296dc..bc22b90f 100644 --- a/NadekoBot/NadekoBot.csproj +++ b/NadekoBot/NadekoBot.csproj @@ -56,6 +56,7 @@ TRACE prompt 4 + true true @@ -189,6 +190,7 @@ + diff --git a/NadekoBot/_Models/JSONModels/Configuration.cs b/NadekoBot/_Models/JSONModels/Configuration.cs index d39c6e00..dbe8455b 100644 --- a/NadekoBot/_Models/JSONModels/Configuration.cs +++ b/NadekoBot/_Models/JSONModels/Configuration.cs @@ -90,6 +90,17 @@ namespace NadekoBot.Classes.JSONModels public bool IsRotatingStatus { get; set; } = false; public int BufferSize { get; set; } = 4.MiB(); + public string[] RaceAnimals { get; internal set; } = { + "🐼", + "🐻", + "🐧", + "🐨", + "🐬", + "🐞", + "πŸ¦€", + "πŸ¦„" }; + + [JsonIgnore] public List Quotes { get; set; } = new List(); [JsonIgnore] diff --git a/NadekoBot/bin/Debug/data/config_example.json b/NadekoBot/bin/Debug/data/config_example.json index 1ae8a26e..9d6995c2 100644 --- a/NadekoBot/bin/Debug/data/config_example.json +++ b/NadekoBot/bin/Debug/data/config_example.json @@ -4,7 +4,16 @@ "ForwardToAllOwners": false, "IsRotatingStatus": false, "BufferSize": 4194304, - "Quotes": [], + "RaceAnimals": [ + "🐼", + "🐻", + "🐧", + "🐨", + "🐬", + "🐞", + "πŸ¦€", + "πŸ¦„" + ], "RemindMessageFormat": "❗⏰**I've been told to remind you to '%message%' now by %user%.**⏰❗", "CustomReactions": { "\\o\\": [