2016-02-02 11:23:58 +00:00
|
|
|
|
using Discord;
|
|
|
|
|
using NadekoBot.Extensions;
|
|
|
|
|
using System.Collections.Concurrent;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
2016-03-02 05:00:44 +00:00
|
|
|
|
using System.Text;
|
2016-02-02 11:23:58 +00:00
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
2016-08-20 20:35:27 +00:00
|
|
|
|
namespace NadekoBot.Modules.Games.Trivia
|
2016-03-26 02:25:03 +00:00
|
|
|
|
{
|
2016-08-16 13:29:48 +00:00
|
|
|
|
public class TriviaGame
|
2016-03-26 02:25:03 +00:00
|
|
|
|
{
|
2016-08-16 13:29:48 +00:00
|
|
|
|
private readonly SemaphoreSlim _guessLock = new SemaphoreSlim(1, 1);
|
2016-02-02 11:23:58 +00:00
|
|
|
|
|
2016-08-16 13:29:48 +00:00
|
|
|
|
private IGuild guild { get; }
|
|
|
|
|
private ITextChannel channel { get; }
|
2016-02-02 11:23:58 +00:00
|
|
|
|
|
|
|
|
|
private int QuestionDurationMiliseconds { get; } = 30000;
|
|
|
|
|
private int HintTimeoutMiliseconds { get; } = 6000;
|
2016-04-09 14:55:22 +00:00
|
|
|
|
public bool ShowHints { get; set; } = true;
|
2016-02-02 11:23:58 +00:00
|
|
|
|
private CancellationTokenSource triviaCancelSource { get; set; }
|
|
|
|
|
|
|
|
|
|
public TriviaQuestion CurrentQuestion { get; private set; }
|
2016-03-03 17:24:58 +00:00
|
|
|
|
public HashSet<TriviaQuestion> oldQuestions { get; } = new HashSet<TriviaQuestion>();
|
2016-02-02 11:23:58 +00:00
|
|
|
|
|
2016-08-16 13:29:48 +00:00
|
|
|
|
public ConcurrentDictionary<IGuildUser, int> Users { get; } = new ConcurrentDictionary<IGuildUser, int>();
|
2016-02-02 11:23:58 +00:00
|
|
|
|
|
|
|
|
|
public bool GameActive { get; private set; } = false;
|
|
|
|
|
public bool ShouldStopGame { get; private set; }
|
|
|
|
|
|
2016-02-04 17:24:06 +00:00
|
|
|
|
public int WinRequirement { get; } = 10;
|
2016-02-02 11:23:58 +00:00
|
|
|
|
|
2016-08-16 13:29:48 +00:00
|
|
|
|
public TriviaGame(IGuild guild, ITextChannel channel, bool showHints, int winReq = 10)
|
2016-03-26 02:25:03 +00:00
|
|
|
|
{
|
2016-04-09 14:55:22 +00:00
|
|
|
|
ShowHints = showHints;
|
2016-08-16 13:29:48 +00:00
|
|
|
|
this.guild = guild;
|
|
|
|
|
this.channel = channel;
|
2016-05-11 10:25:30 +00:00
|
|
|
|
WinRequirement = winReq;
|
2016-03-02 05:00:44 +00:00
|
|
|
|
Task.Run(StartGame);
|
2016-02-02 11:23:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-03-26 02:25:03 +00:00
|
|
|
|
private async Task StartGame()
|
|
|
|
|
{
|
|
|
|
|
while (!ShouldStopGame)
|
|
|
|
|
{
|
2016-02-02 11:23:58 +00:00
|
|
|
|
// reset the cancellation source
|
|
|
|
|
triviaCancelSource = new CancellationTokenSource();
|
2016-02-28 07:47:47 +00:00
|
|
|
|
var token = triviaCancelSource.Token;
|
2016-02-02 11:23:58 +00:00
|
|
|
|
// load question
|
|
|
|
|
CurrentQuestion = TriviaQuestionPool.Instance.GetRandomQuestion(oldQuestions);
|
2016-03-26 02:25:03 +00:00
|
|
|
|
if (CurrentQuestion == null)
|
|
|
|
|
{
|
2016-08-16 13:29:48 +00:00
|
|
|
|
await channel.SendMessageAsync($":exclamation: Failed loading a trivia question").ConfigureAwait(false);
|
2016-04-18 21:38:19 +00:00
|
|
|
|
await End().ConfigureAwait(false);
|
2016-02-02 11:23:58 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
oldQuestions.Add(CurrentQuestion); //add it to exclusion list so it doesn't show up again
|
|
|
|
|
//sendquestion
|
2016-08-16 13:29:48 +00:00
|
|
|
|
await channel.SendMessageAsync($":question: **{CurrentQuestion.Question}**").ConfigureAwait(false);
|
2016-02-02 11:23:58 +00:00
|
|
|
|
|
|
|
|
|
//receive messages
|
2016-02-29 09:59:40 +00:00
|
|
|
|
NadekoBot.Client.MessageReceived += PotentialGuess;
|
2016-02-02 11:23:58 +00:00
|
|
|
|
|
|
|
|
|
//allow people to guess
|
|
|
|
|
GameActive = true;
|
|
|
|
|
|
2016-03-26 02:25:03 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
2016-02-02 11:23:58 +00:00
|
|
|
|
//hint
|
2016-04-18 21:38:19 +00:00
|
|
|
|
await Task.Delay(HintTimeoutMiliseconds, token).ConfigureAwait(false);
|
2016-04-09 14:55:22 +00:00
|
|
|
|
if (ShowHints)
|
2016-08-16 13:29:48 +00:00
|
|
|
|
await channel.SendMessageAsync($":exclamation:**Hint:** {CurrentQuestion.GetHint()}").ConfigureAwait(false);
|
2016-02-02 11:23:58 +00:00
|
|
|
|
|
|
|
|
|
//timeout
|
2016-04-18 21:38:19 +00:00
|
|
|
|
await Task.Delay(QuestionDurationMiliseconds - HintTimeoutMiliseconds, token).ConfigureAwait(false);
|
2016-02-02 11:23:58 +00:00
|
|
|
|
|
2016-03-26 02:25:03 +00:00
|
|
|
|
}
|
2016-06-04 01:16:47 +00:00
|
|
|
|
catch (TaskCanceledException) { } //means someone guessed the answer
|
2016-02-28 07:47:47 +00:00
|
|
|
|
GameActive = false;
|
|
|
|
|
if (!triviaCancelSource.IsCancellationRequested)
|
2016-08-16 13:29:48 +00:00
|
|
|
|
await channel.SendMessageAsync($":clock2: :question: **Time's up!** The correct answer was **{CurrentQuestion.Answer}**").ConfigureAwait(false);
|
2016-02-29 09:59:40 +00:00
|
|
|
|
NadekoBot.Client.MessageReceived -= PotentialGuess;
|
2016-02-02 11:23:58 +00:00
|
|
|
|
// load next question if game is still running
|
2016-04-18 21:38:19 +00:00
|
|
|
|
await Task.Delay(2000).ConfigureAwait(false);
|
2016-02-02 11:23:58 +00:00
|
|
|
|
}
|
2016-04-18 21:38:19 +00:00
|
|
|
|
await End().ConfigureAwait(false);
|
2016-02-02 11:23:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-03-26 02:25:03 +00:00
|
|
|
|
private async Task End()
|
|
|
|
|
{
|
2016-02-23 07:33:16 +00:00
|
|
|
|
ShouldStopGame = true;
|
2016-08-16 13:29:48 +00:00
|
|
|
|
await channel.SendMessageAsync("**Trivia game ended**\n" + GetLeaderboard()).ConfigureAwait(false);
|
2016-02-02 11:23:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-03-26 02:25:03 +00:00
|
|
|
|
public async Task StopGame()
|
|
|
|
|
{
|
2016-02-02 11:23:58 +00:00
|
|
|
|
if (!ShouldStopGame)
|
2016-08-16 13:29:48 +00:00
|
|
|
|
await channel.SendMessageAsync(":exclamation: Trivia will stop after this question.").ConfigureAwait(false);
|
2016-02-02 11:23:58 +00:00
|
|
|
|
ShouldStopGame = true;
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-10 19:40:25 +00:00
|
|
|
|
private Task PotentialGuess(IMessage imsg)
|
2016-03-26 02:25:03 +00:00
|
|
|
|
{
|
2016-08-28 02:33:09 +00:00
|
|
|
|
var umsg = imsg as IUserMessage;
|
|
|
|
|
if (umsg == null)
|
2016-09-10 19:40:25 +00:00
|
|
|
|
return Task.CompletedTask;
|
2016-09-04 15:23:58 +00:00
|
|
|
|
var t = Task.Run(async () =>
|
2016-03-26 02:25:03 +00:00
|
|
|
|
{
|
2016-07-23 13:04:33 +00:00
|
|
|
|
try
|
2016-03-26 02:25:03 +00:00
|
|
|
|
{
|
2016-09-04 15:23:58 +00:00
|
|
|
|
if (!(umsg.Channel is IGuildChannel && umsg.Channel is ITextChannel)) return;
|
|
|
|
|
if ((umsg.Channel as ITextChannel).Guild != guild) return;
|
2016-09-30 03:58:44 +00:00
|
|
|
|
if (umsg.Author.Id == NadekoBot.Client.GetCurrentUser().Id) return;
|
2016-09-04 15:23:58 +00:00
|
|
|
|
|
|
|
|
|
var guildUser = umsg.Author as IGuildUser;
|
|
|
|
|
|
|
|
|
|
var guess = false;
|
|
|
|
|
await _guessLock.WaitAsync().ConfigureAwait(false);
|
|
|
|
|
try
|
2016-03-26 02:25:03 +00:00
|
|
|
|
{
|
2016-09-04 15:23:58 +00:00
|
|
|
|
if (GameActive && CurrentQuestion.IsAnswerCorrect(umsg.Content) && !triviaCancelSource.IsCancellationRequested)
|
|
|
|
|
{
|
|
|
|
|
Users.AddOrUpdate(guildUser, 0, (gu, old) => old++);
|
|
|
|
|
guess = true;
|
|
|
|
|
}
|
2016-02-26 08:46:50 +00:00
|
|
|
|
}
|
2016-09-04 15:23:58 +00:00
|
|
|
|
finally { _guessLock.Release(); }
|
|
|
|
|
if (!guess) return;
|
|
|
|
|
triviaCancelSource.Cancel();
|
|
|
|
|
await channel.SendMessageAsync($"☑️ {guildUser.Mention} guessed it! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false);
|
|
|
|
|
if (Users[guildUser] != WinRequirement) return;
|
|
|
|
|
ShouldStopGame = true;
|
|
|
|
|
await channel.SendMessageAsync($":exclamation: We have a winner! It's {guildUser.Mention}.").ConfigureAwait(false);
|
2016-02-02 11:23:58 +00:00
|
|
|
|
}
|
2016-09-04 15:23:58 +00:00
|
|
|
|
catch { }
|
|
|
|
|
});
|
2016-09-10 19:40:25 +00:00
|
|
|
|
return Task.CompletedTask;
|
2016-02-02 11:23:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-03-26 02:25:03 +00:00
|
|
|
|
public string GetLeaderboard()
|
|
|
|
|
{
|
2016-03-02 05:00:44 +00:00
|
|
|
|
if (Users.Count == 0)
|
2016-02-02 11:23:58 +00:00
|
|
|
|
return "";
|
|
|
|
|
|
2016-03-02 05:00:44 +00:00
|
|
|
|
var sb = new StringBuilder();
|
|
|
|
|
sb.Append("**Leaderboard:**\n-----------\n");
|
2016-02-02 11:23:58 +00:00
|
|
|
|
|
2016-03-26 02:25:03 +00:00
|
|
|
|
foreach (var kvp in Users.OrderBy(kvp => kvp.Value))
|
|
|
|
|
{
|
2016-08-16 13:29:48 +00:00
|
|
|
|
sb.AppendLine($"**{kvp.Key.Username}** has {kvp.Value} points".ToString().SnPl(kvp.Value));
|
2016-02-02 11:23:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-03-02 05:00:44 +00:00
|
|
|
|
return sb.ToString();
|
2016-02-02 11:23:58 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|