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 ;
2016-10-05 05:01:19 +00:00
using NLog ;
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-12-08 17:35:34 +00:00
public static ConcurrentDictionary < ulong , AnimalRace > AnimalRaces { get ; } = new ConcurrentDictionary < ulong , AnimalRace > ( ) ;
2016-07-25 02:21:37 +00:00
2016-10-05 03:09:44 +00:00
[NadekoCommand, Usage, Description, Aliases]
2016-08-20 15:02:33 +00:00
[RequireContext(ContextType.Guild)]
2016-12-16 18:43:57 +00:00
public async Task Race ( )
2016-08-20 15:02:33 +00:00
{
2016-12-16 21:44:26 +00:00
//var channel = (ITextChannel)Context.Channel;
2016-07-25 02:21:37 +00:00
2016-12-16 21:44:26 +00:00
var ar = new AnimalRace ( Context . Guild . Id , channel ) ;
2016-07-25 02:21:37 +00:00
2016-08-20 15:02:33 +00:00
if ( ar . Fail )
2016-12-16 21:44:26 +00:00
await Context . Channel . SendErrorAsync ( "🏁 `Failed starting a race. Another race is probably running.`" ) ;
2016-08-20 15:02:33 +00:00
}
2016-07-25 02:21:37 +00:00
2016-10-05 03:09:44 +00:00
[NadekoCommand, Usage, Description, Aliases]
2016-08-20 15:02:33 +00:00
[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-12-16 21:44:26 +00:00
//var channel = (ITextChannel)Context.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-07-29 11:06:24 +00:00
2016-08-20 15:02:33 +00:00
AnimalRace ar ;
2016-12-16 21:44:26 +00:00
if ( ! AnimalRaces . TryGetValue ( Context . Guild . Id , out ar ) )
2016-08-20 15:02:33 +00:00
{
2016-12-16 21:44:26 +00:00
await Context . Channel . SendErrorAsync ( "No race exists on this server" ) ;
2016-08-20 15:02:33 +00:00
return ;
}
2016-12-16 18:43:57 +00:00
await ar . JoinRace ( Context . User 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-09-01 01:12:08 +00:00
public bool Fail { get ; 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-10-05 05:01:19 +00:00
private Logger _log { get ; }
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-10-05 05:01:19 +00:00
this . _log = LogManager . GetCurrentClassLogger ( ) ;
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 ( ) )
{
2016-10-05 05:01:19 +00:00
animals = new ConcurrentQueue < string > ( uow . BotConfig . GetOrCreate ( ) . RaceAnimals . Select ( ra = > ra . Icon ) . Shuffle ( ) ) ;
2016-08-28 00:46:36 +00:00
}
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-12-11 16:18:25 +00:00
try { await raceChannel . SendConfirmAsync ( $"🏁`Race is starting in 20 seconds or when the room is full. Type {NadekoBot.ModulePrefixes[typeof(Gambling).Name]}jr to join the race.`" ) ; } catch ( Exception ex ) { _log . Warn ( ex ) ; }
2016-08-20 15:02:33 +00:00
var t = await Task . WhenAny ( Task . Delay ( 20000 , token ) , fullgame ) ;
Started = true ;
cancelSource . Cancel ( ) ;
if ( t = = fullgame )
{
2016-12-11 16:18:25 +00:00
try { await raceChannel . SendConfirmAsync ( "🏁`Race full, starting right now!`" ) ; } catch ( Exception ex ) { _log . Warn ( ex ) ; }
2016-08-20 15:02:33 +00:00
}
else if ( participants . Count > 1 )
{
2016-12-11 16:18:25 +00:00
try { await raceChannel . SendConfirmAsync ( "🏁`Game starting with " + participants . Count + " participants.`" ) ; } catch ( Exception ex ) { _log . Warn ( ex ) ; }
2016-08-20 15:02:33 +00:00
}
else
{
2016-12-11 16:18:25 +00:00
try { await raceChannel . SendErrorAsync ( "🏁`Race failed to start since there was not enough participants.`" ) ; } catch ( Exception ex ) { _log . Warn ( ex ) ; }
2016-08-20 15:02:33 +00:00
var p = participants . FirstOrDefault ( ) ;
2016-10-05 05:01:19 +00:00
2016-11-15 00:06:07 +00:00
if ( p ! = null & & p . AmountBet > 0 )
await CurrencyHandler . AddCurrencyAsync ( p . User , "BetRace" , p . AmountBet , false ) . 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-12-08 17:35:34 +00:00
catch { try { End ( ) ; } catch { } }
2016-08-20 15:02:33 +00:00
} ) ;
}
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 ( )
{
2016-09-10 19:40:25 +00:00
var rng = new NadekoRandom ( ) ;
2016-08-20 15:02:33 +00:00
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 { }
messagesSinceGameStarted = 0 ;
2016-10-05 05:01:19 +00:00
try { msg = await raceChannel . SendMessageAsync ( text ) . ConfigureAwait ( false ) ; } catch ( Exception ex ) { _log . Warn ( ex ) ; }
2016-08-20 15:02:33 +00:00
}
else
2016-10-05 05:01:19 +00:00
{
try { await msg . ModifyAsync ( m = > m . Content = text ) . ConfigureAwait ( false ) ; } catch ( Exception ex ) { _log . Warn ( ex ) ; }
}
2016-08-20 15:02:33 +00:00
await Task . Delay ( 2500 ) ;
2016-07-25 02:21:37 +00:00
}
2016-08-20 15:02:33 +00:00
}
2016-10-24 21:05:49 +00:00
catch { }
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-12-11 16:18:25 +00:00
await raceChannel . SendConfirmAsync ( $"🏁 {winner.User.Mention} as {winner.Animal} **Won the race and {wonAmount}{CurrencySign}!**" ) . ConfigureAwait ( false ) ;
2016-08-20 15:02:33 +00:00
}
else
{
2016-12-11 16:18:25 +00:00
await raceChannel . SendConfirmAsync ( $"🏁 {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-09-10 19:40:25 +00:00
private 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 )
2016-09-10 19:40:25 +00:00
return Task . CompletedTask ;
2016-12-16 18:43:57 +00:00
if ( msg . IsAuthor ( ) | | ! ( Context . Channel is ITextChannel ) | | Context . Channel ! = raceChannel )
2016-09-10 19:40:25 +00:00
return Task . CompletedTask ;
2016-08-20 15:02:33 +00:00
messagesSinceGameStarted + + ;
2016-09-10 19:40:25 +00:00
return Task . CompletedTask ;
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
2016-10-26 16:17:40 +00:00
public async Task 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 ) )
{
2016-12-11 16:18:25 +00:00
await raceChannel . SendErrorAsync ( $"{u.Mention} `There is no running race on this server.`" ) ;
2016-10-26 16:17:40 +00:00
return ;
2016-08-20 15:02:33 +00:00
}
var p = new Participant ( u , animal , amount ) ;
if ( participants . Contains ( p ) )
{
2016-12-11 16:18:25 +00:00
await raceChannel . SendErrorAsync ( $"{u.Mention} `You already joined this race.`" ) ;
2016-10-26 16:17:40 +00:00
return ;
2016-08-20 15:02:33 +00:00
}
if ( Started )
{
2016-12-11 16:18:25 +00:00
await raceChannel . SendErrorAsync ( $"{u.Mention} `Race is already started`" ) ;
2016-10-26 16:17:40 +00:00
return ;
2016-08-20 15:02:33 +00:00
}
2016-10-26 16:17:40 +00:00
if ( amount > 0 )
if ( ! await CurrencyHandler . RemoveCurrencyAsync ( ( IGuildUser ) u , "BetRace" , amount , true ) . ConfigureAwait ( false ) )
{
2016-12-11 16:18:25 +00:00
try { await raceChannel . SendErrorAsync ( $"{u.Mention} You don't have enough {Gambling.CurrencyName}s." ) . ConfigureAwait ( false ) ; } catch { }
2016-10-26 16:17:40 +00:00
return ;
}
2016-08-20 15:02:33 +00:00
participants . Add ( p ) ;
2016-12-11 16:18:25 +00:00
await raceChannel . SendConfirmAsync ( $"{u.Mention} **joined the race as a {p.Animal}" + ( amount > 0 ? $" and bet {amount} {CurrencySign}!**" : "**" ) ) ;
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
}