2016-07-29 11:06:24 +00:00
using Discord ;
using Discord.Commands ;
2016-08-20 15:02:33 +00:00
using NadekoBot.Attributes ;
2016-07-29 11:06:24 +00:00
using NadekoBot.Extensions ;
2016-08-28 00:46:36 +00:00
using NadekoBot.Services ;
using NadekoBot.Services.Database ;
2016-07-25 02:21:37 +00:00
using System ;
2016-07-29 11:06:24 +00:00
using System.Collections.Concurrent ;
2016-07-25 02:21:37 +00:00
using System.Collections.Generic ;
using System.Linq ;
using System.Threading ;
2016-07-29 11:06:24 +00:00
using System.Threading.Tasks ;
2016-07-25 02:21:37 +00:00
2016-08-20 20:35:27 +00:00
namespace NadekoBot.Modules.Gambling
2016-07-25 02:21:37 +00:00
{
2016-08-20 15:02:33 +00:00
public partial class Gambling
2016-07-25 02:21:37 +00:00
{
2016-08-20 15:02:33 +00:00
[Group]
public class AnimalRacing
2016-07-25 02:21:37 +00:00
{
2016-08-20 15:02:33 +00:00
public static ConcurrentDictionary < ulong , AnimalRace > AnimalRaces = new ConcurrentDictionary < ulong , AnimalRace > ( ) ;
2016-07-25 02:21:37 +00:00
2016-08-20 15:02:33 +00:00
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
[RequireContext(ContextType.Guild)]
2016-08-28 02:33:09 +00:00
public async Task Race ( IUserMessage umsg )
2016-08-20 15:02:33 +00:00
{
2016-08-28 02:33:09 +00:00
var channel = ( ITextChannel ) umsg . Channel ;
2016-07-25 02:21:37 +00:00
2016-08-20 15:02:33 +00:00
var ar = new AnimalRace ( channel . Guild . Id , channel ) ;
2016-07-25 02:21:37 +00:00
2016-08-20 15:02:33 +00:00
if ( ar . Fail )
await channel . SendMessageAsync ( "🏁 `Failed starting a race. Another race is probably running.`" ) ;
}
2016-07-25 02:21:37 +00:00
2016-08-20 15:02:33 +00:00
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
[RequireContext(ContextType.Guild)]
2016-08-28 02:33:09 +00:00
public async Task JoinRace ( IUserMessage umsg , int amount = 0 )
2016-08-20 15:02:33 +00:00
{
2016-08-28 02:33:09 +00:00
var channel = ( ITextChannel ) umsg . Channel ;
2016-07-25 23:00:55 +00:00
2016-08-20 15:02:33 +00:00
if ( amount < 0 )
amount = 0 ;
2016-07-25 23:00:55 +00:00
2016-08-29 22:00:19 +00:00
if ( amount > 0 )
if ( ! await CurrencyHandler . RemoveCurrencyAsync ( ( IGuildUser ) umsg . Author , "BetRace" , amount , true ) . ConfigureAwait ( false ) )
await channel . SendMessageAsync ( $"{umsg.Author.Mention} You don't have enough {Gambling.CurrencyName}s." ) . ConfigureAwait ( false ) ;
2016-07-25 23:00:55 +00:00
2016-07-29 11:06:24 +00:00
2016-08-20 15:02:33 +00:00
AnimalRace ar ;
if ( ! AnimalRaces . TryGetValue ( channel . Guild . Id , out ar ) )
{
await channel . SendMessageAsync ( "No race exists on this server" ) ;
return ;
}
2016-08-28 02:33:09 +00:00
await ar . JoinRace ( umsg . Author as IGuildUser , amount ) ;
2016-08-20 15:02:33 +00:00
}
2016-07-25 02:21:37 +00:00
2016-08-20 15:02:33 +00:00
public class AnimalRace
{
2016-07-25 02:21:37 +00:00
2016-08-28 00:46:36 +00:00
private ConcurrentQueue < string > animals { get ; }
2016-07-25 02:21:37 +00:00
2016-08-20 15:02:33 +00:00
public bool Fail { get ; internal set ; }
2016-07-25 02:21:37 +00:00
2016-08-20 15:02:33 +00:00
public List < Participant > participants = new List < Participant > ( ) ;
private ulong serverId ;
private int messagesSinceGameStarted = 0 ;
2016-07-25 02:21:37 +00:00
2016-08-20 15:02:33 +00:00
public ITextChannel raceChannel { get ; set ; }
public bool Started { get ; private set ; } = false ;
2016-07-25 02:21:37 +00:00
2016-08-20 15:02:33 +00:00
public AnimalRace ( ulong serverId , ITextChannel ch )
2016-07-25 02:21:37 +00:00
{
2016-08-20 15:02:33 +00:00
this . serverId = serverId ;
this . raceChannel = ch ;
if ( ! AnimalRaces . TryAdd ( serverId , this ) )
2016-07-25 02:21:37 +00:00
{
2016-08-20 15:02:33 +00:00
Fail = true ;
return ;
}
2016-08-28 00:46:36 +00:00
using ( var uow = DbHandler . UnitOfWork ( ) )
{
animals = new ConcurrentQueue < string > ( uow . BotConfig . GetOrCreate ( ) . RaceAnimals . Select ( ra = > ra . Icon ) . Shuffle ( ) ) ;
}
2016-08-20 15:02:33 +00:00
var cancelSource = new CancellationTokenSource ( ) ;
var token = cancelSource . Token ;
var fullgame = CheckForFullGameAsync ( token ) ;
Task . Run ( async ( ) = >
{
try
2016-07-25 23:32:40 +00:00
{
2016-08-20 15:02:33 +00:00
//todo Commmand prefixes from config
await raceChannel . SendMessageAsync ( $"🏁`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 . SendMessageAsync ( "🏁`Race full, starting right now!`" ) ;
}
else if ( participants . Count > 1 )
{
await raceChannel . SendMessageAsync ( "🏁`Game starting with " + participants . Count + " participants.`" ) ;
}
else
{
await raceChannel . SendMessageAsync ( "🏁`Race failed to start since there was not enough participants.`" ) ;
var p = participants . FirstOrDefault ( ) ;
2016-08-29 22:00:19 +00:00
if ( p ! = null )
await CurrencyHandler . AddCurrencyAsync ( p . User , "BetRace" , p . AmountBet , true ) . ConfigureAwait ( false ) ;
2016-08-20 15:02:33 +00:00
End ( ) ;
return ;
}
await Task . Run ( StartRace ) ;
2016-07-25 23:32:40 +00:00
End ( ) ;
}
2016-08-20 15:02:33 +00:00
catch { }
} ) ;
}
2016-07-25 02:21:37 +00:00
2016-08-20 15:02:33 +00:00
private void End ( )
2016-07-25 02:21:37 +00:00
{
2016-08-20 15:02:33 +00:00
AnimalRace throwaway ;
AnimalRaces . TryRemove ( serverId , out throwaway ) ;
}
2016-07-25 02:21:37 +00:00
2016-08-20 15:02:33 +00:00
private async Task StartRace ( )
{
var rng = new Random ( ) ;
Participant winner = null ;
2016-08-28 02:33:09 +00:00
IUserMessage msg = null ;
2016-08-20 15:02:33 +00:00
int place = 1 ;
try
2016-07-25 02:21:37 +00:00
{
2016-08-20 15:02:33 +00:00
NadekoBot . Client . MessageReceived + = Client_MessageReceived ;
2016-07-25 02:21:37 +00:00
2016-08-20 15:02:33 +00:00
while ( ! participants . All ( p = > p . Total > = 60 ) )
{
//update the state
participants . ForEach ( p = >
2016-07-25 02:21:37 +00:00
{
2016-08-20 15:02:33 +00:00
p . Total + = 1 + rng . Next ( 0 , 10 ) ;
if ( p . Total > 60 )
2016-07-25 02:21:37 +00:00
{
2016-08-20 15:02:33 +00:00
p . Total = 60 ;
if ( winner = = null )
{
winner = p ;
}
if ( p . Place = = 0 )
p . Place = place + + ;
2016-07-25 02:21:37 +00:00
}
2016-08-20 15:02:33 +00:00
} ) ;
2016-07-25 02:21:37 +00:00
2016-08-20 15:02:33 +00:00
//draw the state
2016-07-25 02:21:37 +00:00
2016-08-20 15:02:33 +00:00
var text = $ @ "|🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🔚|
2016-07-25 02:21:37 +00:00
{ String . Join ( "\n" , participants . Select ( p = > $"{(int)(p.Total / 60f * 100),-2}%|{p.ToString()}" ) ) }
| 🏁 🏁 🏁 🏁 🏁 🏁 🏁 🏁 🏁 🏁 🏁 🏁 🏁 🏁 🏁 🔚 | ";
2016-08-20 15:02:33 +00:00
if ( msg = = null | | messagesSinceGameStarted > = 10 ) // also resend the message if channel was spammed
{
if ( msg ! = null )
try { await msg . DeleteAsync ( ) ; } catch { }
msg = await raceChannel . SendMessageAsync ( text ) . ConfigureAwait ( false ) ;
messagesSinceGameStarted = 0 ;
}
else
await msg . ModifyAsync ( m = > m . Content = text ) . ConfigureAwait ( false ) ;
await Task . Delay ( 2500 ) ;
2016-07-25 02:21:37 +00:00
}
2016-08-20 15:02:33 +00:00
}
finally
{
NadekoBot . Client . MessageReceived - = Client_MessageReceived ;
}
2016-07-29 11:06:24 +00:00
2016-08-20 15:02:33 +00:00
if ( winner . AmountBet > 0 )
{
var wonAmount = winner . AmountBet * ( participants . Count - 1 ) ;
2016-08-29 22:00:19 +00:00
await CurrencyHandler . AddCurrencyAsync ( winner . User , "Won a Race" , wonAmount , false ) . ConfigureAwait ( false ) ;
2016-08-28 00:46:36 +00:00
await raceChannel . SendMessageAsync ( $"🏁 {winner.User.Mention} as {winner.Animal} **Won the race and {wonAmount}{CurrencySign}!**" ) . ConfigureAwait ( false ) ;
2016-08-20 15:02:33 +00:00
}
else
{
await raceChannel . SendMessageAsync ( $"🏁 {winner.User.Mention} as {winner.Animal} **Won the race!**" ) ;
2016-07-25 02:21:37 +00:00
}
2016-07-25 23:00:55 +00:00
}
2016-07-25 02:21:37 +00:00
2016-08-20 15:02:33 +00:00
private async Task Client_MessageReceived ( IMessage imsg )
2016-07-25 02:21:37 +00:00
{
2016-08-28 02:33:09 +00:00
var msg = imsg as IUserMessage ;
if ( msg = = null )
return ;
if ( await msg . IsAuthor ( ) | | ! ( imsg . Channel is ITextChannel ) | | imsg . Channel ! = raceChannel )
2016-08-20 15:02:33 +00:00
return ;
messagesSinceGameStarted + + ;
2016-07-25 02:21:37 +00:00
}
2016-08-20 15:02:33 +00:00
private async Task CheckForFullGameAsync ( CancellationToken cancelToken )
2016-07-25 02:21:37 +00:00
{
2016-08-20 15:02:33 +00:00
while ( animals . Count > 0 )
{
await Task . Delay ( 100 , cancelToken ) ;
}
2016-07-25 02:21:37 +00:00
}
2016-08-20 15:02:33 +00:00
public async Task < bool > JoinRace ( IGuildUser u , int amount = 0 )
2016-07-25 02:21:37 +00:00
{
2016-08-20 15:02:33 +00:00
var animal = "" ;
if ( ! animals . TryDequeue ( out animal ) )
{
await raceChannel . SendMessageAsync ( $"{u.Mention} `There is no running race on this server.`" ) ;
return false ;
}
var p = new Participant ( u , animal , amount ) ;
if ( participants . Contains ( p ) )
{
await raceChannel . SendMessageAsync ( $"{u.Mention} `You already joined this race.`" ) ;
return false ;
}
if ( Started )
{
await raceChannel . SendMessageAsync ( $"{u.Mention} `Race is already started`" ) ;
return false ;
}
participants . Add ( p ) ;
2016-08-28 00:46:36 +00:00
await raceChannel . SendMessageAsync ( $"{u.Mention} **joined the race as a {p.Animal}" + ( amount > 0 ? $" and bet {amount} {(amount == 1? CurrencyName : CurrencyPluralName)}!**" : "**" ) ) ;
2016-08-20 15:02:33 +00:00
return true ;
2016-07-25 02:21:37 +00:00
}
}
2016-08-20 15:02:33 +00:00
public class Participant
2016-07-25 02:21:37 +00:00
{
2016-08-20 15:02:33 +00:00
public IGuildUser User { get ; set ; }
public string Animal { get ; set ; }
public int AmountBet { get ; set ; }
2016-07-25 02:21:37 +00:00
2016-08-20 15:02:33 +00:00
public float Coeff { get ; set ; }
public int Total { get ; set ; }
2016-07-25 02:21:37 +00:00
2016-08-20 15:02:33 +00:00
public int Place { get ; set ; } = 0 ;
2016-07-25 02:21:37 +00:00
2016-08-20 15:02:33 +00:00
public Participant ( IGuildUser u , string a , int amount )
2016-07-25 02:21:37 +00:00
{
2016-08-20 15:02:33 +00:00
this . User = u ;
this . Animal = a ;
this . AmountBet = amount ;
2016-07-25 02:21:37 +00:00
}
2016-08-20 15:02:33 +00:00
public override int GetHashCode ( )
2016-07-25 02:21:37 +00:00
{
2016-08-20 15:02:33 +00:00
return User . GetHashCode ( ) ;
2016-07-25 02:21:37 +00:00
}
2016-08-20 15:02:33 +00:00
public override bool Equals ( object obj )
2016-07-25 02:21:37 +00:00
{
2016-08-20 15:02:33 +00:00
var p = obj as Participant ;
return p = = null ?
false :
p . User = = User ;
2016-07-25 02:21:37 +00:00
}
2016-08-20 15:02:33 +00:00
public override string ToString ( )
2016-07-29 11:06:24 +00:00
{
2016-08-20 15:02:33 +00:00
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`" ;
}
2016-07-25 02:21:37 +00:00
2016-08-20 15:02:33 +00:00
}
2016-07-25 02:21:37 +00:00
}
}
}
2016-08-20 15:02:33 +00:00
}