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\\": [