2016-11-25 00:20:18 +00:00
using Discord.Commands ;
2016-11-26 00:52:27 +00:00
using NadekoBot.Attributes ;
2016-11-25 00:20:18 +00:00
using NadekoBot.Extensions ;
2016-11-26 00:52:27 +00:00
using System.Linq ;
using NadekoBot.Services ;
using NadekoBot.Services.Database.Models ;
2016-11-25 00:20:18 +00:00
using System.Collections.Generic ;
2016-11-27 02:51:10 +00:00
using System.Threading.Tasks ;
using Discord ;
using NLog ;
using System ;
using Newtonsoft.Json ;
using System.IO ;
2016-11-26 00:52:27 +00:00
using System.Collections.Concurrent ;
2016-11-25 00:20:18 +00:00
namespace NadekoBot.Modules.Pokemon
{
2016-11-26 00:52:27 +00:00
[NadekoModule("PokeGame", ">")]
public partial class PokemonModule : DiscordModule
{
public static string CurrencyName { get ; set ; }
public static string CurrencyPluralName { get ; set ; }
public static string CurrencySign { get ; set ; }
2016-11-27 02:51:10 +00:00
private static List < PokemonType > PokemonTypes = new List < PokemonType > ( ) ;
private static ConcurrentDictionary < ulong , PokeStats > Stats = new ConcurrentDictionary < ulong , PokeStats > ( ) ;
public const string PokemonTypesFile = "data/pokemon_types.json" ;
private Logger _pokelog { get ; }
public PokemonModule ( ILocalization loc , CommandService cmds , ShardedDiscordClient client ) : base ( loc , cmds , client )
2016-11-26 00:52:27 +00:00
{
2016-11-27 02:51:10 +00:00
_pokelog = LogManager . GetCurrentClassLogger ( ) ;
if ( File . Exists ( PokemonTypesFile ) )
2016-11-26 00:52:27 +00:00
{
2016-11-27 02:51:10 +00:00
PokemonTypes = JsonConvert . DeserializeObject < List < PokemonType > > ( File . ReadAllText ( PokemonTypesFile ) ) ;
}
else
{
_pokelog . Warn ( PokemonTypesFile + " is missing. Pokemon types not loaded." ) ;
2016-11-26 00:52:27 +00:00
}
2016-11-28 01:15:08 +00:00
using ( var uow = DbHandler . UnitOfWork ( ) )
{
var conf = uow . BotConfig . GetOrCreate ( ) ;
CurrencyName = conf . CurrencyName ;
CurrencySign = conf . CurrencySign ;
CurrencyPluralName = conf . CurrencyPluralName ;
}
2016-11-26 00:52:27 +00:00
}
2016-11-25 00:20:18 +00:00
private int GetDamage ( PokemonType usertype , PokemonType targetType )
{
var rng = new Random ( ) ;
int damage = rng . Next ( 40 , 60 ) ;
foreach ( PokemonMultiplier Multiplier in usertype . Multipliers )
{
if ( Multiplier . Type = = targetType . Name )
{
var multiplier = Multiplier . Multiplication ;
damage = ( int ) ( damage * multiplier ) ;
}
}
return damage ;
}
2016-11-27 02:51:10 +00:00
2016-11-25 00:20:18 +00:00
private PokemonType GetPokeType ( ulong id )
{
2016-11-27 02:51:10 +00:00
Dictionary < long , string > setTypes ;
using ( var uow = DbHandler . UnitOfWork ( ) )
{
setTypes = uow . PokeGame . GetAll ( ) . ToDictionary ( x = > x . UserId , y = > y . type ) ;
}
2016-11-25 00:20:18 +00:00
if ( setTypes . ContainsKey ( ( long ) id ) )
{
return stringToPokemonType ( setTypes [ ( long ) id ] ) ;
}
2016-11-27 02:51:10 +00:00
int count = PokemonTypes . Count ;
2016-11-25 00:20:18 +00:00
int remainder = Math . Abs ( ( int ) ( id % ( ulong ) count ) ) ;
2016-11-27 02:51:10 +00:00
return PokemonTypes [ remainder ] ;
2016-11-25 00:20:18 +00:00
}
private PokemonType stringToPokemonType ( string v )
{
var str = v . ToUpperInvariant ( ) ;
2016-11-27 02:51:10 +00:00
var list = PokemonTypes ;
2016-11-25 00:20:18 +00:00
foreach ( PokemonType p in list )
{
if ( str = = p . Name )
{
return p ;
}
}
return null ;
}
2016-11-27 02:51:10 +00:00
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Attack ( IUserMessage umsg , string move , IGuildUser targetUser = null )
2016-11-25 00:20:18 +00:00
{
2016-11-27 02:51:10 +00:00
var channel = ( ITextChannel ) umsg . Channel ;
IGuildUser user = ( IGuildUser ) umsg . Author ;
if ( string . IsNullOrWhiteSpace ( move ) ) {
return ;
}
if ( targetUser = = null )
2016-11-25 00:20:18 +00:00
{
2016-11-27 02:51:10 +00:00
await channel . SendMessageAsync ( "No such person." ) . ConfigureAwait ( false ) ;
return ;
}
else if ( targetUser = = user )
{
await channel . SendMessageAsync ( "You can't attack yourself." ) . ConfigureAwait ( false ) ;
return ;
}
2016-11-25 00:20:18 +00:00
2016-11-27 02:51:10 +00:00
// Checking stats first, then move
//Set up the userstats
PokeStats userStats ;
userStats = Stats . GetOrAdd ( user . Id , new PokeStats ( ) ) ;
2016-11-25 00:20:18 +00:00
2016-11-27 02:51:10 +00:00
//Check if able to move
//User not able if HP < 0, has made more than 4 attacks
if ( userStats . Hp < 0 )
{
await channel . SendMessageAsync ( $"{user.Mention} has fainted and was not able to move!" ) . ConfigureAwait ( false ) ;
return ;
}
if ( userStats . MovesMade > = 5 )
{
await channel . SendMessageAsync ( $"{user.Mention} has used too many moves in a row and was not able to move!" ) . ConfigureAwait ( false ) ;
return ;
}
if ( userStats . LastAttacked . Contains ( targetUser . Id ) )
{
await channel . SendMessageAsync ( $"{user.Mention} can't attack again without retaliation!" ) . ConfigureAwait ( false ) ;
return ;
}
//get target stats
PokeStats targetStats ;
targetStats = Stats . GetOrAdd ( targetUser . Id , new PokeStats ( ) ) ;
2016-11-25 00:20:18 +00:00
2016-11-27 02:51:10 +00:00
//If target's HP is below 0, no use attacking
if ( targetStats . Hp < = 0 )
{
await channel . SendMessageAsync ( $"{targetUser.Mention} has already fainted!" ) . ConfigureAwait ( false ) ;
return ;
}
2016-11-25 00:20:18 +00:00
2016-11-27 02:51:10 +00:00
//Check whether move can be used
PokemonType userType = GetPokeType ( user . Id ) ;
2016-11-25 00:20:18 +00:00
2016-11-27 02:51:10 +00:00
var enabledMoves = userType . Moves ;
if ( ! enabledMoves . Contains ( move . ToLowerInvariant ( ) ) )
{
await channel . SendMessageAsync ( $"{user.Mention} is not able to use **{move}**. Type {NadekoBot.ModulePrefixes[typeof(PokemonModule).Name]}ml to see moves" ) . ConfigureAwait ( false ) ;
return ;
}
2016-11-25 00:20:18 +00:00
2016-11-27 02:51:10 +00:00
//get target type
PokemonType targetType = GetPokeType ( targetUser . Id ) ;
//generate damage
int damage = GetDamage ( userType , targetType ) ;
//apply damage to target
targetStats . Hp - = damage ;
2016-11-25 00:20:18 +00:00
2016-11-27 02:51:10 +00:00
var response = $"{user.Mention} used **{move}**{userType.Icon} on {targetUser.Mention}{targetType.Icon} for **{damage}** damage" ;
2016-11-25 00:20:18 +00:00
2016-11-27 02:51:10 +00:00
//Damage type
if ( damage < 40 )
{
response + = "\nIt's not effective.." ;
}
else if ( damage > 60 )
{
response + = "\nIt's super effective!" ;
}
else
{
response + = "\nIt's somewhat effective" ;
}
2016-11-25 00:20:18 +00:00
2016-11-27 02:51:10 +00:00
//check fainted
2016-11-25 00:20:18 +00:00
2016-11-27 02:51:10 +00:00
if ( targetStats . Hp < = 0 )
{
2016-11-28 01:15:08 +00:00
response + = $"\n**{targetUser.Nickname}** has fainted!" ;
2016-11-27 02:51:10 +00:00
}
else
{
2016-11-28 01:15:08 +00:00
response + = $"\n**{targetUser.Nickname}** has {targetStats.Hp} HP remaining" ;
2016-11-27 02:51:10 +00:00
}
2016-11-25 00:20:18 +00:00
2016-11-27 02:51:10 +00:00
//update other stats
userStats . LastAttacked . Add ( targetUser . Id ) ;
userStats . MovesMade + + ;
targetStats . MovesMade = 0 ;
if ( targetStats . LastAttacked . Contains ( user . Id ) )
{
targetStats . LastAttacked . Remove ( user . Id ) ;
}
2016-11-25 00:20:18 +00:00
2016-11-27 02:51:10 +00:00
//update dictionary
//This can stay the same right?
Stats [ user . Id ] = userStats ;
Stats [ targetUser . Id ] = targetStats ;
2016-11-25 00:20:18 +00:00
2016-11-27 02:51:10 +00:00
await channel . SendMessageAsync ( response ) . ConfigureAwait ( false ) ;
}
2016-11-25 00:20:18 +00:00
2016-11-27 02:51:10 +00:00
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Movelist ( IUserMessage umsg )
{
var channel = ( ITextChannel ) umsg . Channel ;
IGuildUser user = ( IGuildUser ) umsg . Author ;
2016-11-25 00:20:18 +00:00
2016-11-27 02:51:10 +00:00
var userType = GetPokeType ( user . Id ) ;
var movesList = userType . Moves ;
var str = $"**Moves for `{userType.Name}` type.**" ;
foreach ( string m in movesList )
{
str + = $"\n{userType.Icon}{m}" ;
}
await channel . SendMessageAsync ( str ) . ConfigureAwait ( false ) ;
}
2016-11-25 00:20:18 +00:00
2016-11-27 02:51:10 +00:00
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Heal ( IUserMessage umsg , IGuildUser targetUser = null )
{
var channel = ( ITextChannel ) umsg . Channel ;
IGuildUser user = ( IGuildUser ) umsg . Author ;
2016-11-25 00:20:18 +00:00
2016-11-27 02:51:10 +00:00
if ( targetUser = = null ) {
await channel . SendMessageAsync ( "No such person." ) . ConfigureAwait ( false ) ;
return ;
}
2016-11-25 00:20:18 +00:00
2016-11-27 02:51:10 +00:00
if ( Stats . ContainsKey ( targetUser . Id ) )
{
var targetStats = Stats [ targetUser . Id ] ;
if ( targetStats . Hp = = targetStats . MaxHp )
{
2016-11-28 01:15:08 +00:00
await channel . SendMessageAsync ( $"{targetUser.Nickname} already has full HP!" ) . ConfigureAwait ( false ) ;
2016-11-27 02:51:10 +00:00
return ;
}
//Payment~
var amount = 1 ;
2016-11-25 00:20:18 +00:00
2016-11-28 01:15:08 +00:00
var target = ( targetUser . Id = = user . Id ) ? "yourself" : targetUser . Nickname ;
2016-11-27 02:51:10 +00:00
if ( amount > 0 )
{
if ( ! await CurrencyHandler . RemoveCurrencyAsync ( user , $"Poke-Heal {target}" , amount , true ) . ConfigureAwait ( false ) )
2016-11-25 00:20:18 +00:00
{
2016-11-27 02:51:10 +00:00
try { await channel . SendMessageAsync ( $"{user.Mention} You don't have enough {CurrencyName}s." ) . ConfigureAwait ( false ) ; } catch { }
2016-11-25 00:20:18 +00:00
return ;
}
2016-11-27 02:51:10 +00:00
}
2016-11-25 00:20:18 +00:00
2016-11-27 02:51:10 +00:00
//healing
targetStats . Hp = targetStats . MaxHp ;
if ( targetStats . Hp < 0 )
{
//Could heal only for half HP?
Stats [ targetUser . Id ] . Hp = ( targetStats . MaxHp / 2 ) ;
if ( target = = "yourself" )
{
await channel . SendMessageAsync ( $"You revived yourself with one {CurrencySign}" ) . ConfigureAwait ( false ) ;
}
else
{
2016-11-28 01:15:08 +00:00
await channel . SendMessageAsync ( $"{user.Nickname} revived {targetUser.Nickname} with one {CurrencySign}" ) . ConfigureAwait ( false ) ;
2016-11-27 02:51:10 +00:00
}
return ;
}
2016-11-28 01:15:08 +00:00
await channel . SendMessageAsync ( $"{user.Nickname} healed {targetUser.Nickname} with one {CurrencySign}" ) . ConfigureAwait ( false ) ;
2016-11-27 02:51:10 +00:00
return ;
}
else
{
2016-11-28 01:15:08 +00:00
await channel . SendMessageAsync ( $"{targetUser.Nickname} already has full HP!" ) . ConfigureAwait ( false ) ;
2016-11-27 02:51:10 +00:00
}
}
2016-11-25 00:20:18 +00:00
2016-11-27 02:51:10 +00:00
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Type ( IUserMessage umsg , IGuildUser targetUser = null )
{
var channel = ( ITextChannel ) umsg . Channel ;
IGuildUser user = ( IGuildUser ) umsg . Author ;
if ( targetUser = = null )
{
return ;
}
var pType = GetPokeType ( targetUser . Id ) ;
2016-11-28 01:15:08 +00:00
await channel . SendMessageAsync ( $"Type of {targetUser.Nickname} is **{pType.Name.ToLowerInvariant()}**{pType.Icon}" ) . ConfigureAwait ( false ) ;
2016-11-27 02:51:10 +00:00
2016-11-25 00:20:18 +00:00
}
2016-11-27 02:51:10 +00:00
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Settype ( IUserMessage umsg , [ Remainder ] string typeTargeted = null )
{
var channel = ( ITextChannel ) umsg . Channel ;
IGuildUser user = ( IGuildUser ) umsg . Author ;
if ( string . IsNullOrWhiteSpace ( typeTargeted ) )
return ;
var targetType = stringToPokemonType ( typeTargeted ) ;
if ( targetType = = null )
{
await channel . SendMessageAsync ( "Invalid type specified. Type must be one of:\n" + string . Join ( ", " , PokemonTypes . Select ( t = > t . Name . ToUpperInvariant ( ) ) ) ) . ConfigureAwait ( false ) ;
return ;
}
if ( targetType = = GetPokeType ( user . Id ) )
{
await channel . SendMessageAsync ( $"Your type is already {targetType.Name.ToLowerInvariant()}{targetType.Icon}" ) . ConfigureAwait ( false ) ;
return ;
}
//Payment~
var amount = 1 ;
if ( amount > 0 )
{
2016-11-28 01:15:08 +00:00
if ( ! await CurrencyHandler . RemoveCurrencyAsync ( user , $"{user.Nickname} change type to {typeTargeted}" , amount , true ) . ConfigureAwait ( false ) )
2016-11-27 02:51:10 +00:00
{
try { await channel . SendMessageAsync ( $"{user.Mention} You don't have enough {CurrencyName}s." ) . ConfigureAwait ( false ) ; } catch { }
return ;
}
}
//Actually changing the type here
Dictionary < long , string > setTypes ;
using ( var uow = DbHandler . UnitOfWork ( ) )
{
2016-11-28 01:15:08 +00:00
var pokeUsers = uow . PokeGame . GetAll ( ) ;
setTypes = pokeUsers . ToDictionary ( x = > x . UserId , y = > y . type ) ;
2016-11-27 02:51:10 +00:00
var pt = new UserPokeTypes
{
UserId = ( long ) user . Id ,
type = targetType . Name ,
} ;
if ( ! setTypes . ContainsKey ( ( long ) user . Id ) )
{
//create user in db
uow . PokeGame . Add ( pt ) ;
}
else
{
//update user in db
2016-11-28 01:15:08 +00:00
var pokeUserCmd = pokeUsers . Where ( p = > p . UserId = = ( long ) user . Id ) . FirstOrDefault ( ) ;
pokeUserCmd . type = targetType . Name ;
uow . PokeGame . Update ( pokeUserCmd ) ;
2016-11-27 02:51:10 +00:00
}
await uow . CompleteAsync ( ) ;
}
//Now for the response
await channel . SendMessageAsync ( $"Set type of {user.Mention} to {typeTargeted}{targetType.Icon} for a {CurrencySign}" ) . ConfigureAwait ( false ) ;
}
2016-11-25 00:20:18 +00:00
}
}