Trivia improved quite a bit. Questions changed. More questions to come in the future via api. closes #180
This commit is contained in:
parent
0565830583
commit
4ecc66a3fe
@ -22,7 +22,7 @@ namespace NadekoBot.Modules.Games.Trivia
|
||||
|
||||
private int QuestionDurationMiliseconds { get; } = 30000;
|
||||
private int HintTimeoutMiliseconds { get; } = 6000;
|
||||
public bool ShowHints { get; set; } = true;
|
||||
public bool ShowHints { get; } = true;
|
||||
private CancellationTokenSource triviaCancelSource { get; set; }
|
||||
|
||||
public TriviaQuestion CurrentQuestion { get; private set; }
|
||||
@ -35,95 +35,111 @@ namespace NadekoBot.Modules.Games.Trivia
|
||||
|
||||
public int WinRequirement { get; } = 10;
|
||||
|
||||
public TriviaGame(IGuild guild, ITextChannel channel, bool showHints, int winReq = 10)
|
||||
public TriviaGame(IGuild guild, ITextChannel channel, bool showHints, int winReq)
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
ShowHints = showHints;
|
||||
this._log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
this.ShowHints = showHints;
|
||||
this.guild = guild;
|
||||
this.channel = channel;
|
||||
WinRequirement = winReq;
|
||||
Task.Run(async () => { try { await StartGame().ConfigureAwait(false); } catch { } });
|
||||
this.WinRequirement = winReq;
|
||||
}
|
||||
|
||||
private async Task StartGame()
|
||||
public async Task StartGame()
|
||||
{
|
||||
while (!ShouldStopGame)
|
||||
{
|
||||
// reset the cancellation source
|
||||
triviaCancelSource = new CancellationTokenSource();
|
||||
var token = triviaCancelSource.Token;
|
||||
|
||||
// load question
|
||||
CurrentQuestion = TriviaQuestionPool.Instance.GetRandomQuestion(oldQuestions);
|
||||
if (CurrentQuestion == null)
|
||||
{
|
||||
try { await channel.SendErrorAsync($":exclamation: Failed loading a trivia question.").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
|
||||
await End().ConfigureAwait(false);
|
||||
await channel.SendErrorAsync("Trivia Game", "Failed loading a question.").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
oldQuestions.Add(CurrentQuestion); //add it to exclusion list so it doesn't show up again
|
||||
//sendquestion
|
||||
try { await channel.SendConfirmAsync($":question: Question",$"**{CurrentQuestion.Question}**").ConfigureAwait(false); }
|
||||
catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound || ex.StatusCode == System.Net.HttpStatusCode.Forbidden)
|
||||
{
|
||||
break;
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
|
||||
//receive messages
|
||||
NadekoBot.Client.MessageReceived += PotentialGuess;
|
||||
|
||||
//allow people to guess
|
||||
GameActive = true;
|
||||
|
||||
EmbedBuilder questionEmbed;
|
||||
IUserMessage questionMessage;
|
||||
try
|
||||
{
|
||||
//hint
|
||||
await Task.Delay(HintTimeoutMiliseconds, token).ConfigureAwait(false);
|
||||
if (ShowHints)
|
||||
try { await channel.SendConfirmAsync($":exclamation: Hint", CurrentQuestion.GetHint()).ConfigureAwait(false); }
|
||||
catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound || ex.StatusCode == System.Net.HttpStatusCode.Forbidden)
|
||||
{
|
||||
break;
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
|
||||
//timeout
|
||||
await Task.Delay(QuestionDurationMiliseconds - HintTimeoutMiliseconds, token).ConfigureAwait(false);
|
||||
questionEmbed = new EmbedBuilder().WithOkColor()
|
||||
.WithTitle("Trivia Game")
|
||||
.AddField(eab => eab.WithName("Category").WithValue(CurrentQuestion.Category))
|
||||
.AddField(eab => eab.WithName("Question").WithValue(CurrentQuestion.Question));
|
||||
|
||||
questionMessage = await channel.EmbedAsync(questionEmbed.Build()).ConfigureAwait(false);
|
||||
}
|
||||
catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound || ex.StatusCode == System.Net.HttpStatusCode.Forbidden)
|
||||
{
|
||||
return;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn(ex);
|
||||
await Task.Delay(2000).ConfigureAwait(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
//receive messages
|
||||
try
|
||||
{
|
||||
NadekoBot.Client.MessageReceived += PotentialGuess;
|
||||
|
||||
//allow people to guess
|
||||
GameActive = true;
|
||||
try
|
||||
{
|
||||
//hint
|
||||
await Task.Delay(HintTimeoutMiliseconds, triviaCancelSource.Token).ConfigureAwait(false);
|
||||
if (ShowHints)
|
||||
try
|
||||
{
|
||||
await questionMessage.ModifyAsync(m => m.Embed = questionEmbed.WithFooter(efb => efb.WithText(CurrentQuestion.GetHint())).Build())
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound || ex.StatusCode == System.Net.HttpStatusCode.Forbidden)
|
||||
{
|
||||
break;
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
|
||||
//timeout
|
||||
await Task.Delay(QuestionDurationMiliseconds - HintTimeoutMiliseconds, triviaCancelSource.Token).ConfigureAwait(false);
|
||||
|
||||
}
|
||||
catch (TaskCanceledException) { } //means someone guessed the answer
|
||||
}
|
||||
finally
|
||||
{
|
||||
GameActive = false;
|
||||
NadekoBot.Client.MessageReceived -= PotentialGuess;
|
||||
}
|
||||
catch (TaskCanceledException) { } //means someone guessed the answer
|
||||
GameActive = false;
|
||||
if (!triviaCancelSource.IsCancellationRequested)
|
||||
try { await channel.SendConfirmAsync($":clock2: :question: **Time's up!** The correct answer was **{CurrentQuestion.Answer}**").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
|
||||
NadekoBot.Client.MessageReceived -= PotentialGuess;
|
||||
// load next question if game is still running
|
||||
try { await channel.SendErrorAsync("Trivia Game", $"**Time's up!** The correct answer was **{CurrentQuestion.Answer}**").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
|
||||
await Task.Delay(2000).ConfigureAwait(false);
|
||||
}
|
||||
try { NadekoBot.Client.MessageReceived -= PotentialGuess; } catch { }
|
||||
GameActive = false;
|
||||
await End().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task End()
|
||||
public async Task EnsureStopped()
|
||||
{
|
||||
ShouldStopGame = true;
|
||||
TriviaGame throwaway;
|
||||
Games.TriviaCommands.RunningTrivias.TryRemove(channel.Guild.Id, out throwaway);
|
||||
try
|
||||
{
|
||||
await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithTitle("Leaderboard")
|
||||
.WithDescription(GetLeaderboard())
|
||||
.Build(), "Trivia game ended.").ConfigureAwait(false);
|
||||
}
|
||||
catch { }
|
||||
|
||||
await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithAuthor(eab => eab.WithName("Trivia Game Ended"))
|
||||
.WithTitle("Final Results")
|
||||
.WithDescription(GetLeaderboard())
|
||||
.Build()).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task StopGame()
|
||||
{
|
||||
if (!ShouldStopGame)
|
||||
try { await channel.SendConfirmAsync(":exclamation: Trivia will stop after this question.").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
|
||||
var old = ShouldStopGame;
|
||||
ShouldStopGame = true;
|
||||
if (!old)
|
||||
try { await channel.SendConfirmAsync("Trivia Game", "Stopping after this question.").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
|
||||
}
|
||||
|
||||
private Task PotentialGuess(IMessage imsg)
|
||||
@ -137,11 +153,11 @@ namespace NadekoBot.Modules.Games.Trivia
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!(umsg.Channel is IGuildChannel && umsg.Channel is ITextChannel)) return;
|
||||
if ((umsg.Channel as ITextChannel).Guild != guild) return;
|
||||
if (umsg.Author.Id == NadekoBot.Client.GetCurrentUser().Id) return;
|
||||
var textChannel = umsg.Channel as ITextChannel;
|
||||
if (textChannel == null || textChannel.Guild != guild)
|
||||
return;
|
||||
|
||||
var guildUser = umsg.Author as IGuildUser;
|
||||
var guildUser = (IGuildUser)umsg.Author;
|
||||
|
||||
var guess = false;
|
||||
await _guessLock.WaitAsync().ConfigureAwait(false);
|
||||
@ -156,10 +172,15 @@ namespace NadekoBot.Modules.Games.Trivia
|
||||
finally { _guessLock.Release(); }
|
||||
if (!guess) return;
|
||||
triviaCancelSource.Cancel();
|
||||
try { await channel.SendConfirmAsync($"☑️ {guildUser.Mention} guessed it! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
|
||||
if (Users[guildUser] != WinRequirement) return;
|
||||
ShouldStopGame = true;
|
||||
await channel.SendConfirmAsync($":exclamation: We have a winner! It's {guildUser.Mention}.").ConfigureAwait(false);
|
||||
|
||||
|
||||
if (Users[guildUser] == WinRequirement) {
|
||||
ShouldStopGame = true;
|
||||
await channel.SendConfirmAsync("Trivia Game", $"{guildUser.Mention} guessed it and WON the game! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
await channel.SendConfirmAsync("Trivia Game", $"{guildUser.Mention} guessed it! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false);
|
||||
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
});
|
||||
@ -169,7 +190,7 @@ namespace NadekoBot.Modules.Games.Trivia
|
||||
public string GetLeaderboard()
|
||||
{
|
||||
if (Users.Count == 0)
|
||||
return "";
|
||||
return "No results.";
|
||||
|
||||
var sb = new StringBuilder();
|
||||
|
||||
|
@ -18,9 +18,9 @@ namespace NadekoBot.Modules.Games.Trivia
|
||||
};
|
||||
public static int maxStringLength = 22;
|
||||
|
||||
public string Category;
|
||||
public string Question;
|
||||
public string Answer;
|
||||
public string Category { get; set; }
|
||||
public string Question { get; set; }
|
||||
public string Answer { get; set; }
|
||||
|
||||
public TriviaQuestion(string q, string a, string c)
|
||||
{
|
||||
@ -79,9 +79,6 @@ namespace NadekoBot.Modules.Games.Trivia
|
||||
return str;
|
||||
}
|
||||
|
||||
public override string ToString() =>
|
||||
"Question: **" + this.Question + "?**";
|
||||
|
||||
private static string Scramble(string word)
|
||||
{
|
||||
var letters = word.ToArray();
|
||||
@ -101,7 +98,7 @@ namespace NadekoBot.Modules.Games.Trivia
|
||||
if (letters[i] != ' ')
|
||||
letters[i] = '_';
|
||||
}
|
||||
return "`" + string.Join(" ", letters) + "`";
|
||||
return string.Join(" ", letters);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
@ -11,36 +12,31 @@ namespace NadekoBot.Modules.Games.Trivia
|
||||
{
|
||||
public class TriviaQuestionPool
|
||||
{
|
||||
public static TriviaQuestionPool Instance { get; } = new TriviaQuestionPool();
|
||||
public ConcurrentHashSet<TriviaQuestion> pool = new ConcurrentHashSet<TriviaQuestion>();
|
||||
private static TriviaQuestionPool _instance;
|
||||
public static TriviaQuestionPool Instance { get; } = _instance ?? (_instance = new TriviaQuestionPool());
|
||||
|
||||
private const string questionsFile = "data/trivia_questions.json";
|
||||
|
||||
private Random rng { get; } = new NadekoRandom();
|
||||
|
||||
private TriviaQuestion[] pool { get; }
|
||||
|
||||
static TriviaQuestionPool() { }
|
||||
|
||||
private TriviaQuestionPool()
|
||||
{
|
||||
Reload();
|
||||
pool = JsonConvert.DeserializeObject<TriviaQuestion[]>(File.ReadAllText(questionsFile));
|
||||
}
|
||||
|
||||
public TriviaQuestion GetRandomQuestion(IEnumerable<TriviaQuestion> exclude)
|
||||
public TriviaQuestion GetRandomQuestion(HashSet<TriviaQuestion> exclude)
|
||||
{
|
||||
var list = pool.Except(exclude).ToList();
|
||||
var rand = rng.Next(0, list.Count);
|
||||
return list[rand];
|
||||
}
|
||||
if (pool.Length == 0)
|
||||
return null;
|
||||
|
||||
public void Reload()
|
||||
{
|
||||
var arr = JArray.Parse(File.ReadAllText("data/questions.json"));
|
||||
TriviaQuestion randomQuestion;
|
||||
while (exclude.Contains(randomQuestion = pool[rng.Next(0, pool.Length)])) ;
|
||||
|
||||
foreach (var item in arr)
|
||||
{
|
||||
var tq = new TriviaQuestion(item["Question"].ToString().SanitizeMentions(), item["Answer"].ToString().SanitizeMentions(), item["Category"]?.ToString());
|
||||
pool.Add(tq);
|
||||
}
|
||||
var r = new NadekoRandom();
|
||||
pool = new ConcurrentHashSet<TriviaQuestion>(pool.OrderBy(x => r.Next()));
|
||||
return randomQuestion;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,26 +20,29 @@ namespace NadekoBot.Modules.Games
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Trivia(IUserMessage umsg, params string[] args)
|
||||
public Task Trivia(IUserMessage umsg, [Remainder] string additionalArgs = "")
|
||||
=> Trivia(umsg, 10, additionalArgs);
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Trivia(IUserMessage umsg, int winReq = 10, [Remainder] string additionalArgs = "")
|
||||
{
|
||||
var channel = (ITextChannel)umsg.Channel;
|
||||
|
||||
TriviaGame trivia;
|
||||
if (!RunningTrivias.TryGetValue(channel.Guild.Id, out trivia))
|
||||
var showHints = !additionalArgs.Contains("nohint");
|
||||
|
||||
TriviaGame trivia = new TriviaGame(channel.Guild, channel, showHints, winReq);
|
||||
if (RunningTrivias.TryAdd(channel.Guild.Id, trivia))
|
||||
{
|
||||
var showHints = !args.Contains("nohint");
|
||||
var number = args.Select(s =>
|
||||
try
|
||||
{
|
||||
int num;
|
||||
return new Tuple<bool, int>(int.TryParse(s, out num), num);
|
||||
}).Where(t => t.Item1).Select(t => t.Item2).FirstOrDefault();
|
||||
if (number < 0)
|
||||
return;
|
||||
var triviaGame = new TriviaGame(channel.Guild, (ITextChannel)umsg.Channel, showHints, number == 0 ? 10 : number);
|
||||
if (RunningTrivias.TryAdd(channel.Guild.Id, triviaGame))
|
||||
await channel.SendConfirmAsync($"**Trivia game started! {triviaGame.WinRequirement} points needed to win.**").ConfigureAwait(false);
|
||||
else
|
||||
await triviaGame.StopGame().ConfigureAwait(false);
|
||||
await trivia.StartGame().ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
RunningTrivias.TryRemove(channel.Guild.Id, out trivia);
|
||||
await trivia.EnsureStopped().ConfigureAwait(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
1
src/NadekoBot/data/trivia_questions.json
Normal file
1
src/NadekoBot/data/trivia_questions.json
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user