Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Badge trading #231

Open
wants to merge 21 commits into
base: badge-trading
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions TPP.ArgsParsing/TypeParsers/AnyOrderParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ public override async Task<ArgsParseResult<AnyOrder>> Parse(
2 => typeof(AnyOrder<,>),
3 => typeof(AnyOrder<,,>),
4 => typeof(AnyOrder<,,,>),
5 => typeof(AnyOrder<,,,,>),
6 => typeof(AnyOrder<,,,,,>),
var num => throw new InvalidOperationException(
$"An implementation of {typeof(AnyOrder)} for {num} generic arguments " +
"needs to be implemented and wired up where this exception is thrown. " +
Expand Down
37 changes: 37 additions & 0 deletions TPP.ArgsParsing/TypeParsers/BadgeSourceParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using TPP.Persistence.Models;

namespace TPP.ArgsParsing.TypeParsers
{
public class BadgeSourceParser : BaseArgumentParser<Badge.BadgeSource>
{
public override Task<ArgsParseResult<Badge.BadgeSource>> Parse(IImmutableList<string> args, Type[] genericTypes)
{
string source = args[0];
ArgsParseResult<Badge.BadgeSource> result;
Badge.BadgeSource? parsedSource = null;
try
{
parsedSource = (Badge.BadgeSource)Enum.Parse(typeof(Badge.BadgeSource), source, ignoreCase: true);
}
catch (ArgumentException)
{
switch (args[1].ToLower())
{
case "run":
case "caught":
parsedSource = Badge.BadgeSource.RunCaught;
break;
}
}
if (parsedSource != null)
result = ArgsParseResult<Badge.BadgeSource>.Success(parsedSource.Value, args.Skip(1).ToImmutableList());
else
result = ArgsParseResult<Badge.BadgeSource>.Failure($"Did not find a source named '{args[0]}'");
return Task.FromResult(result);
}
}
}
45 changes: 45 additions & 0 deletions TPP.ArgsParsing/TypeParsers/ShinyParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using TPP.Common;
using TPP.ArgsParsing.Types;

namespace TPP.ArgsParsing.TypeParsers
{
/// <summary>
/// A parser that determines if something is indicated to be shiny or not.
/// </summary>
public class ShinyParser : BaseArgumentParser<Shiny>
{
string[] shinyWords =
{
"shiny",
"shiny:true"
};
string[] plainWords =
{
"plain",
"regular",
"shiny:false"
};
public override Task<ArgsParseResult<Shiny>> Parse(IImmutableList<string> args, Type[] genericTypes)
{
string s = args[0];
Mogiiii marked this conversation as resolved.
Show resolved Hide resolved
ArgsParseResult<Shiny> result;
if (shinyWords.Contains(s))
{
result = ArgsParseResult<Shiny>.Success(new Shiny { Value = true }, args.Skip(1).ToImmutableList());
}
else if (plainWords.Contains(s))
{
result = ArgsParseResult<Shiny>.Success(new Shiny { Value = false }, args.Skip(1).ToImmutableList());
}
else
{
result = ArgsParseResult<Shiny>.Failure("The argument couldn't be understood as shiny or not", ErrorRelevanceConfidence.Unlikely);
}
return Task.FromResult(result);
}
}
}
43 changes: 43 additions & 0 deletions TPP.ArgsParsing/Types/AnyOrder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,47 @@ public AnyOrder(T1 item1, T2 item2, T3 item3, T4 item4)
public void Deconstruct(out T1 item1, out T2 item2, out T3 item3, out T4 item4) =>
(item1, item2, item3, item4) = (Item1, Item2, Item3, Item4);
}

public class AnyOrder<T1, T2, T3, T4, T5> : AnyOrder
{
public T1 Item1 { get; }
public T2 Item2 { get; }
public T3 Item3 { get; }
public T4 Item4 { get; }
public T5 Item5 { get; }

public AnyOrder(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5)
{
Item1 = item1;
Item2 = item2;
Item3 = item3;
Item4 = item4;
Item5 = item5;
}

public void Deconstruct(out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5) =>
(item1, item2, item3, item4, item5) = (Item1, Item2, Item3, Item4, Item5);
}
public class AnyOrder<T1, T2, T3, T4, T5, T6> : AnyOrder
{
public T1 Item1 { get; }
public T2 Item2 { get; }
public T3 Item3 { get; }
public T4 Item4 { get; }
public T5 Item5 { get; }
public T6 Item6 { get; }

public AnyOrder(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6)
{
Item1 = item1;
Item2 = item2;
Item3 = item3;
Item4 = item4;
Item5 = item5;
Item6 = item6;
}

public void Deconstruct(out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6) =>
(item1, item2, item3, item4, item5, item6) = (Item1, Item2, Item3, Item4, Item5, Item6);
}
}
19 changes: 19 additions & 0 deletions TPP.ArgsParsing/Types/ImplicitBoolean.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TPP.ArgsParsing.Types
{
public class ImplicitBoolean
{
public bool Value { get; internal init; }
public static implicit operator bool(ImplicitBoolean b) => b.Value;
public override string ToString() => Value.ToString();
}

public class Shiny : ImplicitBoolean
{
}
}
63 changes: 63 additions & 0 deletions TPP.Common/PkmnForms.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using TPP.Common;

namespace TPP.Common
{
public static class PkmnForms
{
/// <summary>
///
/// </summary>
static readonly Dictionary<string, Dictionary<string, int>> Forms = new Dictionary<string, Dictionary<string, int>>
{
["unown"] = new Dictionary<string, int>
{
["a"] = 1,
["b"] = 2,
},
["shellos"] = new Dictionary<string, int>
{
["west sea"] = 1,
["westsea"] = 1,
["west"] = 1,
["pink"] = 1,
["east sea"] = 2,
["eastsea"] = 2,
["east"] = 2,
["blue"] = 2,
},
};

public static string getFormName(PkmnSpecies pokemon, int formid)
{
string pkmnName = pokemon.Name.ToLower(); //TODO: use normalize_name from pkmnspecies
Dictionary<string, int>? forms;
if (!Forms.TryGetValue(pkmnName, out forms))
throw new ArgumentException($"{pokemon.Name} does not have alternate forms.");
string formName = forms.FirstOrDefault(p => p.Value == formid).Key;
if (formName == null)
throw new ArgumentException($"{pokemon.Name} does not have a form with id {formid}.");
return formName;
}

public static int getFormId(PkmnSpecies pokemon, string formName)
{
string pkmnName = pokemon.Name.ToLower();
Dictionary<string, int>? forms;
if (!Forms.TryGetValue(pkmnName, out forms))
throw new ArgumentException($"{pokemon.Name} does not have alternate forms.");
int formid = forms.GetValueOrDefault(formName.ToLower());
Mogiiii marked this conversation as resolved.
Show resolved Hide resolved
if (formid == 0)
throw new ArgumentException($"{pokemon.Name} does not have a form called {formName}.");
return formid;
}

public static bool pokemonHasForms(PkmnSpecies pokemon)
{
return Forms.ContainsKey(pokemon.Name.ToLower());
}
}
}
6 changes: 3 additions & 3 deletions TPP.Core.Tests/Commands/Definitions/BadgeCommandsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -264,9 +264,9 @@ public async Task TestGiftBadgeSuccessful()
User user = MockUser("MockUser");
User recipient = MockUser("Recipient");
_userRepoMock.Setup(repo => repo.FindBySimpleName("recipient")).Returns(Task.FromResult((User?)recipient));
Badge badge1 = new("badge1", user.Id, species, Badge.BadgeSource.ManualCreation, Instant.MinValue);
Badge badge2 = new("badge2", user.Id, species, Badge.BadgeSource.ManualCreation, Instant.MinValue);
Badge badge3 = new("badge3", user.Id, species, Badge.BadgeSource.ManualCreation, Instant.MinValue);
Badge badge1 = new("badge1", user.Id, species, Badge.BadgeSource.ManualCreation, Instant.MinValue, 0, false);
Badge badge2 = new("badge2", user.Id, species, Badge.BadgeSource.ManualCreation, Instant.MinValue, 0, false);
Badge badge3 = new("badge3", user.Id, species, Badge.BadgeSource.ManualCreation, Instant.MinValue, 0, false);
_badgeRepoMock.Setup(repo => repo.FindByUserAndSpecies(user.Id, species))
.Returns(Task.FromResult(new List<Badge> { badge1, badge2, badge3, }));

Expand Down
10 changes: 5 additions & 5 deletions TPP.Core.Tests/Commands/Definitions/OperatorCommandsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,9 @@ public async Task TestTransferBadgeSuccessful()
_messageSenderMock.Object, _badgeRepoMock.Object);
_userRepoMock.Setup(repo => repo.FindBySimpleName("gifter")).Returns(Task.FromResult((User?)gifter));
_userRepoMock.Setup(repo => repo.FindBySimpleName("recipient")).Returns(Task.FromResult((User?)recipient));
Badge badge1 = new("badge1", gifter.Id, species, Badge.BadgeSource.ManualCreation, Instant.MinValue);
Badge badge2 = new("badge2", gifter.Id, species, Badge.BadgeSource.ManualCreation, Instant.MinValue);
Badge badge3 = new("badge3", gifter.Id, species, Badge.BadgeSource.ManualCreation, Instant.MinValue);
Badge badge1 = new("badge1", gifter.Id, species, Badge.BadgeSource.ManualCreation, Instant.MinValue, 0, false);
Badge badge2 = new("badge2", gifter.Id, species, Badge.BadgeSource.ManualCreation, Instant.MinValue, 0, false);
Badge badge3 = new("badge3", gifter.Id, species, Badge.BadgeSource.ManualCreation, Instant.MinValue, 0, false);
_badgeRepoMock.Setup(repo => repo.FindByUserAndSpecies(gifter.Id, species))
.Returns(Task.FromResult(new List<Badge> { badge1, badge2, badge3, }));

Expand Down Expand Up @@ -256,10 +256,10 @@ public async Task TestCreateBadge()
CommandResult result = await operatorCommands.CreateBadge(new CommandContext(MockMessage(user),
ImmutableList.Create("recipient", "species", "123"), _argsParser));

Assert.AreEqual("123 badges of species #001 Species created for user Recipient.", result.Response);
Assert.AreEqual("123 Normal #001 Species badges created for Recipient.", result.Response);
Assert.AreEqual(ResponseTarget.Source, result.ResponseTarget);
_badgeRepoMock.Verify(repo =>
repo.AddBadge(recipient.Id, species, Badge.BadgeSource.ManualCreation, null),
repo.AddBadge(recipient.Id, species, Badge.BadgeSource.ManualCreation, 0, false, null),
Times.Exactly(123));
}
}
Expand Down
30 changes: 25 additions & 5 deletions TPP.Core/Commands/Definitions/BadgeCommands.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
Expand Down Expand Up @@ -48,7 +49,7 @@ public class BadgeCommands : ICommandCollection
new Command("giftbadge", GiftBadge)
{
Description =
"Gift a badge you own to another user with no price. Arguments: <pokemon> <number of badges>(Optional) <username>"
"Gift a badge you own to another user with no price. Arguments: <pokemon> <number of badges>(Optional) <username>" //TODO also update this before merge
},
};

Expand Down Expand Up @@ -271,15 +272,34 @@ public async Task<CommandResult> Pokedex(CommandContext context)
public async Task<CommandResult> GiftBadge(CommandContext context)
{
User gifter = context.Message.User;
(User recipient, PkmnSpecies species, Optional<PositiveInt> amountOpt) =
await context.ParseArgs<AnyOrder<User, PkmnSpecies, Optional<PositiveInt>>>();
(User recipient, PkmnSpecies species, Optional<PositiveInt> amountOpt, Optional<Badge.BadgeSource> sourceOpt, Optional<string> formOpt, Optional<string> formOpt2) =
await context.ParseArgs<AnyOrder<User, PkmnSpecies, Optional<PositiveInt>, Optional<Badge.BadgeSource>, Optional<string>, Optional<string>>>();
int amount = amountOpt.Map(i => i.Number).OrElse(1);

Badge.BadgeSource? source = sourceOpt.IsPresent ? sourceOpt.Value : null;
int? form = null;
if (formOpt.IsPresent)
{
string formName = formOpt.Value;
if (formOpt2.IsPresent)
formName += " " + formOpt2.Value;
try
{
form = PkmnForms.getFormId(species, formName);
}
catch (ArgumentException e)
{
return new CommandResult
{
Response = e.Message
};
}
}
if (recipient == gifter)
return new CommandResult { Response = "You cannot gift to yourself" };

List<Badge> badges = await _badgeRepo.FindByUserAndSpecies(gifter.Id, species);
List<Badge> badges = await _badgeRepo.FindAllByCustom(gifter.Id, species, form, source);
if (badges.Count < amount)
//TODO big improve before merge
return new CommandResult
{
Response = $"You tried to gift {amount} {species} badges, but you only have {badges.Count}."
Expand Down
35 changes: 28 additions & 7 deletions TPP.Core/Commands/Definitions/OperatorCommands.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
Expand Down Expand Up @@ -183,18 +184,38 @@ await _messageSender.SendWhisper(recipient, amount > 1

public async Task<CommandResult> CreateBadge(CommandContext context)
{
(User recipient, PkmnSpecies species, Optional<PositiveInt> amountOpt) =
await context.ParseArgs<AnyOrder<User, PkmnSpecies, Optional<PositiveInt>>>();
(User recipient, PkmnSpecies species, Optional<PositiveInt> amountOpt, Optional<string> formOpt, Optional<Shiny> shinyOpt) =
await context.ParseArgs<AnyOrder<User, PkmnSpecies, Optional<PositiveInt>, Optional<string>, Optional<Shiny>>>();
int amount = amountOpt.Map(i => i.Number).OrElse(1);

int form = PkmnForms.pokemonHasForms(species) ? 0 : 1; // default to the first listed form if form is unspecified
Mogiiii marked this conversation as resolved.
Show resolved Hide resolved
bool shiny = shinyOpt.Value ?? false;
Mogiiii marked this conversation as resolved.
Show resolved Hide resolved
if (formOpt.IsPresent)
{
string formName = formOpt.Value;
try
{
form = PkmnForms.getFormId(species, formName);
}
catch (ArgumentException e)
{
return new CommandResult
{
Response = e.Message
};
}
}
for (int i = 0; i < amount; i++)
await _badgeRepo.AddBadge(recipient.Id, species, Badge.BadgeSource.ManualCreation);
await _badgeRepo.AddBadge(recipient.Id, species, Badge.BadgeSource.ManualCreation, form, shiny);

return new CommandResult
{
Response = amount > 1
? $"{amount} badges of species {species} created for user {recipient.Name}."
: $"Badge of species {species} created for user {recipient.Name}."
Response = PkmnForms.pokemonHasForms(species)
? amount > 1
? $"{amount} {PkmnForms.getFormName(species, form)} {species} badges created for {recipient.Name}."
: $"{PkmnForms.getFormName(species, form)} {species} badge created for {recipient.Name}."
: amount > 1
? $"{amount} {species} badges created for {recipient.Name}."
: $"{species} badge created for {recipient.Name}."
};
}
}
Expand Down
2 changes: 2 additions & 0 deletions TPP.Core/Setups.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public static ArgsParser SetUpArgsParser(IUserRepo userRepo, PokedexData pokedex
argsParser.AddArgumentParser(new SignedPokeyenParser());
argsParser.AddArgumentParser(new SignedTokensParser());
argsParser.AddArgumentParser(new PkmnSpeciesParser(pokedexData.KnownSpecies, PokedexData.NormalizeName));
Mogiiii marked this conversation as resolved.
Show resolved Hide resolved
argsParser.AddArgumentParser(new ShinyParser());
argsParser.AddArgumentParser(new BadgeSourceParser());

argsParser.AddArgumentParser(new AnyOrderParser(argsParser));
argsParser.AddArgumentParser(new OneOfParser(argsParser));
Expand Down
Loading