mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-10 17:28:27 -04:00
Many changes. Will update merge request description with details
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
using Nadeko.Common;
|
using Nadeko.Common;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Gambling.Common;
|
namespace Nadeko.Econ;
|
||||||
|
|
||||||
public class Deck
|
public class Deck
|
||||||
{
|
{
|
||||||
@@ -274,7 +274,7 @@ public class Deck
|
|||||||
|
|
||||||
public string GetValueText()
|
public string GetValueText()
|
||||||
=> _cardNames[Number];
|
=> _cardNames[Number];
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
=> _cardNames[Number] + " Of " + Suit;
|
=> _cardNames[Number] + " Of " + Suit;
|
||||||
|
|
7
src/Nadeko.Econ/Gambling/Betdraw/BetdrawColorGuess.cs
Normal file
7
src/Nadeko.Econ/Gambling/Betdraw/BetdrawColorGuess.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Nadeko.Econ.Gambling.Betdraw;
|
||||||
|
|
||||||
|
public enum BetdrawColorGuess
|
||||||
|
{
|
||||||
|
Red,
|
||||||
|
Black
|
||||||
|
}
|
83
src/Nadeko.Econ/Gambling/Betdraw/BetdrawGame.cs
Normal file
83
src/Nadeko.Econ/Gambling/Betdraw/BetdrawGame.cs
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
namespace Nadeko.Econ.Gambling.Betdraw;
|
||||||
|
|
||||||
|
public sealed class BetdrawGame
|
||||||
|
{
|
||||||
|
private static readonly NadekoRandom _rng = new();
|
||||||
|
|
||||||
|
private const decimal SINGLE_GUESS_MULTI = 2.075M;
|
||||||
|
private const decimal DOUBLE_GUESS_MULTI = 4.15M;
|
||||||
|
|
||||||
|
public BetdrawGame()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public BetdrawResult Draw(BetdrawValueGuess? val, BetdrawColorGuess? col, decimal amount)
|
||||||
|
{
|
||||||
|
if (val is null && col is null)
|
||||||
|
throw new ArgumentNullException(nameof(val));
|
||||||
|
|
||||||
|
var card = new Deck().CardPool[_rng.Next(0, 52)];
|
||||||
|
|
||||||
|
var realVal = card.Number < 7
|
||||||
|
? BetdrawValueGuess.Low
|
||||||
|
: BetdrawValueGuess.High;
|
||||||
|
|
||||||
|
var realCol = card.Suit is Deck.CardSuit.Diamonds or Deck.CardSuit.Hearts
|
||||||
|
? BetdrawColorGuess.Red
|
||||||
|
: BetdrawColorGuess.Black;
|
||||||
|
|
||||||
|
// if card is 7, autoloss
|
||||||
|
if (card.Number == 7)
|
||||||
|
{
|
||||||
|
return new()
|
||||||
|
{
|
||||||
|
Won = 0M,
|
||||||
|
Multiplier = 0M,
|
||||||
|
ResultType = BetdrawResultType.Lose,
|
||||||
|
Card = card,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
byte win = 0;
|
||||||
|
if (val is BetdrawValueGuess valGuess)
|
||||||
|
{
|
||||||
|
if (realVal != valGuess)
|
||||||
|
return new()
|
||||||
|
{
|
||||||
|
Won = 0M,
|
||||||
|
Multiplier = 0M,
|
||||||
|
ResultType = BetdrawResultType.Lose,
|
||||||
|
Card = card
|
||||||
|
};
|
||||||
|
|
||||||
|
++win;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (col is BetdrawColorGuess colGuess)
|
||||||
|
{
|
||||||
|
if (realCol != colGuess)
|
||||||
|
return new()
|
||||||
|
{
|
||||||
|
Won = 0M,
|
||||||
|
Multiplier = 0M,
|
||||||
|
ResultType = BetdrawResultType.Lose,
|
||||||
|
Card = card
|
||||||
|
};
|
||||||
|
|
||||||
|
++win;
|
||||||
|
}
|
||||||
|
|
||||||
|
var multi = win == 1
|
||||||
|
? SINGLE_GUESS_MULTI
|
||||||
|
: DOUBLE_GUESS_MULTI;
|
||||||
|
|
||||||
|
return new()
|
||||||
|
{
|
||||||
|
Won = amount * multi,
|
||||||
|
Multiplier = multi,
|
||||||
|
ResultType = BetdrawResultType.Win,
|
||||||
|
Card = card
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
9
src/Nadeko.Econ/Gambling/Betdraw/BetdrawResult.cs
Normal file
9
src/Nadeko.Econ/Gambling/Betdraw/BetdrawResult.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Nadeko.Econ.Gambling.Betdraw;
|
||||||
|
|
||||||
|
public readonly struct BetdrawResult
|
||||||
|
{
|
||||||
|
public decimal Won { get; init; }
|
||||||
|
public decimal Multiplier { get; init; }
|
||||||
|
public BetdrawResultType ResultType { get; init; }
|
||||||
|
public Deck.Card Card { get; init; }
|
||||||
|
}
|
7
src/Nadeko.Econ/Gambling/Betdraw/BetdrawResultType.cs
Normal file
7
src/Nadeko.Econ/Gambling/Betdraw/BetdrawResultType.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Nadeko.Econ.Gambling.Betdraw;
|
||||||
|
|
||||||
|
public enum BetdrawResultType
|
||||||
|
{
|
||||||
|
Win,
|
||||||
|
Lose
|
||||||
|
}
|
7
src/Nadeko.Econ/Gambling/Betdraw/BetdrawValueGuess.cs
Normal file
7
src/Nadeko.Econ/Gambling/Betdraw/BetdrawValueGuess.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Nadeko.Econ.Gambling.Betdraw;
|
||||||
|
|
||||||
|
public enum BetdrawValueGuess
|
||||||
|
{
|
||||||
|
High,
|
||||||
|
Low,
|
||||||
|
}
|
@@ -3,26 +3,31 @@
|
|||||||
public sealed class BetflipGame
|
public sealed class BetflipGame
|
||||||
{
|
{
|
||||||
private readonly decimal _winMulti;
|
private readonly decimal _winMulti;
|
||||||
private readonly NadekoRandom _rng;
|
private static readonly NadekoRandom _rng = new NadekoRandom();
|
||||||
|
|
||||||
public BetflipGame(decimal winMulti)
|
public BetflipGame(decimal winMulti)
|
||||||
{
|
{
|
||||||
_winMulti = winMulti;
|
_winMulti = winMulti;
|
||||||
_rng = new NadekoRandom();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BetflipResult Flip(byte guess, decimal amount)
|
public BetflipResult Flip(byte guess, decimal amount)
|
||||||
{
|
{
|
||||||
var side = _rng.Next(0, 2);
|
var side = _rng.Next(0, 2);
|
||||||
decimal won = 0;
|
|
||||||
|
|
||||||
if (side == guess)
|
if (side == guess)
|
||||||
won = amount * _winMulti;
|
{
|
||||||
|
return new BetflipResult()
|
||||||
|
{
|
||||||
|
Side = side,
|
||||||
|
Won = amount * _winMulti,
|
||||||
|
Multiplier = _winMulti
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return new BetflipResult()
|
return new BetflipResult()
|
||||||
{
|
{
|
||||||
Side = side,
|
Side = side,
|
||||||
Won = won,
|
Won = 0,
|
||||||
|
Multiplier = 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -4,4 +4,5 @@ public readonly struct BetflipResult
|
|||||||
{
|
{
|
||||||
public decimal Won { get; init; }
|
public decimal Won { get; init; }
|
||||||
public byte Side { get; init; }
|
public byte Side { get; init; }
|
||||||
|
public decimal Multiplier { get; init; }
|
||||||
}
|
}
|
@@ -2,15 +2,14 @@
|
|||||||
|
|
||||||
public sealed class LulaGame
|
public sealed class LulaGame
|
||||||
{
|
{
|
||||||
public static IReadOnlyList<decimal> DEFAULT_MULTIPLIERS = new[] { 1.7M, 1.5M, 0.2M, 0.1M, 0.3M, 0.5M, 1.2M, 2.4M };
|
private static readonly IReadOnlyList<decimal> DEFAULT_MULTIPLIERS = new[] { 1.7M, 1.5M, 0.2M, 0.1M, 0.3M, 0.5M, 1.2M, 2.4M };
|
||||||
|
|
||||||
private readonly IReadOnlyList<decimal> _multipliers;
|
private readonly IReadOnlyList<decimal> _multipliers;
|
||||||
private readonly NadekoRandom _rng;
|
private static readonly NadekoRandom _rng = new();
|
||||||
|
|
||||||
public LulaGame(IReadOnlyList<decimal> multipliers)
|
public LulaGame(IReadOnlyList<decimal> multipliers)
|
||||||
{
|
{
|
||||||
_multipliers = multipliers;
|
_multipliers = multipliers;
|
||||||
_rng = new();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public LulaGame() : this(DEFAULT_MULTIPLIERS)
|
public LulaGame() : this(DEFAULT_MULTIPLIERS)
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
using Nadeko.Econ;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Gambling.Common.Blackjack;
|
namespace NadekoBot.Modules.Gambling.Common.Blackjack;
|
||||||
|
|
||||||
public class Blackjack
|
public class Blackjack
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
using Nadeko.Econ;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Gambling.Common.Blackjack;
|
namespace NadekoBot.Modules.Gambling.Common.Blackjack;
|
||||||
|
|
||||||
public abstract class Player
|
public abstract class Player
|
||||||
|
@@ -30,14 +30,23 @@ public partial class Gambling
|
|||||||
|
|
||||||
var num1 = gen / 10;
|
var num1 = gen / 10;
|
||||||
var num2 = gen % 10;
|
var num2 = gen % 10;
|
||||||
|
|
||||||
using var img1 = await GetDiceAsync(num1);
|
using var img1 = await GetDiceAsync(num1);
|
||||||
using var img2 = await GetDiceAsync(num2);
|
using var img2 = await GetDiceAsync(num2);
|
||||||
using var img = new[] { img1, img2 }.Merge(out var format);
|
using var img = new[] { img1, img2 }.Merge(out var format);
|
||||||
await using var ms = await img.ToStreamAsync(format);
|
await using var ms = await img.ToStreamAsync(format);
|
||||||
|
|
||||||
|
var fileName = $"dice.{format.FileExtensions.First()}";
|
||||||
|
|
||||||
|
var eb = _eb.Create(ctx)
|
||||||
|
.WithOkColor()
|
||||||
|
.WithAuthor(ctx.User)
|
||||||
|
.AddField(GetText(strs.roll2), gen)
|
||||||
|
.WithImageUrl($"attachment://{fileName}");
|
||||||
|
|
||||||
await ctx.Channel.SendFileAsync(ms,
|
await ctx.Channel.SendFileAsync(ms,
|
||||||
$"dice.{format.FileExtensions.First()}",
|
fileName,
|
||||||
Format.Bold(ctx.User.ToString()) + " " + GetText(strs.dice_rolled(Format.Code(gen.ToString()))));
|
embed: eb.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
@@ -105,14 +114,18 @@ public partial class Gambling
|
|||||||
foreach (var d in dice)
|
foreach (var d in dice)
|
||||||
d.Dispose();
|
d.Dispose();
|
||||||
|
|
||||||
|
var imageName = $"dice.{format.FileExtensions.First()}";
|
||||||
|
var eb = _eb.Create(ctx)
|
||||||
|
.WithOkColor()
|
||||||
|
.WithAuthor(ctx.User)
|
||||||
|
.AddField(GetText(strs.rolls), values.Select(x => Format.Code(x.ToString())).Join(' '), true)
|
||||||
|
.AddField(GetText(strs.total), values.Sum(), true)
|
||||||
|
.WithDescription(GetText(strs.dice_rolled_num(Format.Bold(values.Count.ToString()))))
|
||||||
|
.WithImageUrl($"attachment://{imageName}");
|
||||||
|
|
||||||
await ctx.Channel.SendFileAsync(ms,
|
await ctx.Channel.SendFileAsync(ms,
|
||||||
$"dice.{format.FileExtensions.First()}",
|
imageName,
|
||||||
Format.Bold(ctx.User.ToString())
|
embed: eb.Build());
|
||||||
+ " "
|
|
||||||
+ GetText(strs.dice_rolled_num(Format.Bold(values.Count.ToString())))
|
|
||||||
+ " "
|
|
||||||
+ GetText(strs.total_average(Format.Bold(values.Sum().ToString()),
|
|
||||||
Format.Bold((values.Sum() / (1.0f * values.Count)).ToString("N2")))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task InternallDndRoll(string arg, bool ordered)
|
private async Task InternallDndRoll(string arg, bool ordered)
|
||||||
@@ -130,9 +143,8 @@ public partial class Gambling
|
|||||||
rolls.Add(_fateRolls[rng.Next(0, _fateRolls.Length)]);
|
rolls.Add(_fateRolls[rng.Next(0, _fateRolls.Length)]);
|
||||||
var embed = _eb.Create()
|
var embed = _eb.Create()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithDescription(ctx.User.Mention
|
.WithAuthor(ctx.User)
|
||||||
+ " "
|
.WithDescription(GetText(strs.dice_rolled_num(Format.Bold(n1.ToString()))))
|
||||||
+ GetText(strs.dice_rolled_num(Format.Bold(n1.ToString()))))
|
|
||||||
.AddField(Format.Bold("Result"),
|
.AddField(Format.Bold("Result"),
|
||||||
string.Join(" ", rolls.Select(c => Format.Code($"[{c}]"))));
|
string.Join(" ", rolls.Select(c => Format.Code($"[{c}]"))));
|
||||||
|
|
||||||
@@ -160,10 +172,9 @@ public partial class Gambling
|
|||||||
var sum = arr.Sum();
|
var sum = arr.Sum();
|
||||||
var embed = _eb.Create()
|
var embed = _eb.Create()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithDescription(ctx.User.Mention
|
.WithAuthor(ctx.User)
|
||||||
+ " "
|
.WithDescription(GetText(strs.dice_rolled_num(n1 + $"`1 - {n2}`")))
|
||||||
+ GetText(strs.dice_rolled_num(n1 + $"`1 - {n2}`")))
|
.AddField(Format.Bold(GetText(strs.rolls)),
|
||||||
.AddField(Format.Bold("Rolls"),
|
|
||||||
string.Join(" ",
|
string.Join(" ",
|
||||||
(ordered ? arr.OrderBy(x => x).AsEnumerable() : arr).Select(x
|
(ordered ? arr.OrderBy(x => x).AsEnumerable() : arr).Select(x
|
||||||
=> Format.Code(x.ToString()))))
|
=> Format.Code(x.ToString()))))
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
using Nadeko.Econ;
|
||||||
using NadekoBot.Modules.Gambling.Common;
|
using NadekoBot.Modules.Gambling.Common;
|
||||||
|
using NadekoBot.Modules.Gambling.Services;
|
||||||
using SixLabors.ImageSharp;
|
using SixLabors.ImageSharp;
|
||||||
using SixLabors.ImageSharp.PixelFormats;
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
using Image = SixLabors.ImageSharp.Image;
|
using Image = SixLabors.ImageSharp.Image;
|
||||||
@@ -9,23 +11,23 @@ namespace NadekoBot.Modules.Gambling;
|
|||||||
public partial class Gambling
|
public partial class Gambling
|
||||||
{
|
{
|
||||||
[Group]
|
[Group]
|
||||||
public partial class DrawCommands : NadekoModule
|
public partial class DrawCommands : GamblingSubmodule<IGamblingService>
|
||||||
{
|
{
|
||||||
private static readonly ConcurrentDictionary<IGuild, Deck> _allDecks = new();
|
private static readonly ConcurrentDictionary<IGuild, Deck> _allDecks = new();
|
||||||
private readonly IImageCache _images;
|
private readonly IImageCache _images;
|
||||||
|
|
||||||
public DrawCommands(IImageCache images)
|
public DrawCommands(IImageCache images, GamblingConfigService gcs) : base(gcs)
|
||||||
=> _images = images;
|
=> _images = images;
|
||||||
|
|
||||||
private async Task<(Stream ImageStream, string ToSend)> InternalDraw(int num, ulong? guildId = null)
|
private async Task InternalDraw(int count, ulong? guildId = null)
|
||||||
{
|
{
|
||||||
if (num is < 1 or > 10)
|
if (count is < 1 or > 10)
|
||||||
throw new ArgumentOutOfRangeException(nameof(num));
|
throw new ArgumentOutOfRangeException(nameof(count));
|
||||||
|
|
||||||
var cards = guildId is null ? new() : _allDecks.GetOrAdd(ctx.Guild, _ => new());
|
var cards = guildId is null ? new() : _allDecks.GetOrAdd(ctx.Guild, _ => new());
|
||||||
var images = new List<Image<Rgba32>>();
|
var images = new List<Image<Rgba32>>();
|
||||||
var cardObjects = new List<Deck.Card>();
|
var cardObjects = new List<Deck.Card>();
|
||||||
for (var i = 0; i < num; i++)
|
for (var i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
if (cards.CardPool.Count == 0 && i != 0)
|
if (cards.CardPool.Count == 0 && i != 0)
|
||||||
{
|
{
|
||||||
@@ -43,22 +45,43 @@ public partial class Gambling
|
|||||||
|
|
||||||
var currentCard = cards.Draw();
|
var currentCard = cards.Draw();
|
||||||
cardObjects.Add(currentCard);
|
cardObjects.Add(currentCard);
|
||||||
var cardName = currentCard.ToString().ToLowerInvariant().Replace(' ', '_');
|
var image = await GetCardImageAsync(currentCard);
|
||||||
images.Add(Image.Load(await File.ReadAllBytesAsync($"data/images/cards/{cardName}.jpg")));
|
images.Add(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var imgName = "cards.jpg";
|
||||||
using var img = images.Merge();
|
using var img = images.Merge();
|
||||||
foreach (var i in images)
|
foreach (var i in images)
|
||||||
i.Dispose();
|
i.Dispose();
|
||||||
|
|
||||||
var toSend = $"{Format.Bold(ctx.User.ToString())}";
|
var eb = _eb.Create(ctx)
|
||||||
|
.WithOkColor();
|
||||||
|
|
||||||
|
var toSend = string.Empty;
|
||||||
if (cardObjects.Count == 5)
|
if (cardObjects.Count == 5)
|
||||||
toSend += $" drew `{Deck.GetHandValue(cardObjects)}`";
|
eb.AddField(GetText(strs.hand_value), Deck.GetHandValue(cardObjects), true);
|
||||||
|
|
||||||
if (guildId is not null)
|
if (guildId is not null)
|
||||||
toSend += "\n" + GetText(strs.cards_left(Format.Bold(cards.CardPool.Count.ToString())));
|
toSend += GetText(strs.cards_left(Format.Bold(cards.CardPool.Count.ToString())));
|
||||||
|
|
||||||
return (img.ToStream(), toSend);
|
eb.WithDescription(toSend)
|
||||||
|
.WithAuthor(ctx.User)
|
||||||
|
.WithImageUrl($"attachment://{imgName}");
|
||||||
|
|
||||||
|
if (count > 1)
|
||||||
|
eb.AddField(GetText(strs.cards), count.ToString(), true);
|
||||||
|
|
||||||
|
await using var imageStream = await img.ToStreamAsync();
|
||||||
|
await ctx.Channel.SendFileAsync(imageStream,
|
||||||
|
imgName,
|
||||||
|
embed: eb.Build());
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<Image<Rgba32>> GetCardImageAsync(Deck.Card currentCard)
|
||||||
|
{
|
||||||
|
var cardName = currentCard.ToString().ToLowerInvariant().Replace(' ', '_');
|
||||||
|
var cardBytes = await File.ReadAllBytesAsync($"data/images/cards/{cardName}.jpg");
|
||||||
|
return Image.Load(cardBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
@@ -66,30 +89,24 @@ public partial class Gambling
|
|||||||
public async partial Task Draw(int num = 1)
|
public async partial Task Draw(int num = 1)
|
||||||
{
|
{
|
||||||
if (num < 1)
|
if (num < 1)
|
||||||
num = 1;
|
return;
|
||||||
|
|
||||||
if (num > 10)
|
if (num > 10)
|
||||||
num = 10;
|
num = 10;
|
||||||
|
|
||||||
var (imageStream, toSend) = await InternalDraw(num, ctx.Guild.Id);
|
await InternalDraw(num, ctx.Guild.Id);
|
||||||
await using (imageStream)
|
|
||||||
{
|
|
||||||
await ctx.Channel.SendFileAsync(imageStream, num + " cards.jpg", toSend);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
public async partial Task DrawNew(int num = 1)
|
public async partial Task DrawNew(int num = 1)
|
||||||
{
|
{
|
||||||
if (num < 1)
|
if (num < 1)
|
||||||
num = 1;
|
return;
|
||||||
|
|
||||||
if (num > 10)
|
if (num > 10)
|
||||||
num = 10;
|
num = 10;
|
||||||
|
|
||||||
var (imageStream, toSend) = await InternalDraw(num);
|
await InternalDraw(num);
|
||||||
await using (imageStream)
|
|
||||||
{
|
|
||||||
await ctx.Channel.SendFileAsync(imageStream, num + " cards.jpg", toSend);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
@@ -108,5 +125,98 @@ public partial class Gambling
|
|||||||
|
|
||||||
await ReplyConfirmLocalizedAsync(strs.deck_reshuffled);
|
await ReplyConfirmLocalizedAsync(strs.deck_reshuffled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public partial Task BetDraw(ShmartNumber amount, InputValueGuess val, InputColorGuess? col = null)
|
||||||
|
=> BetDrawInternal(amount, val, col);
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public partial Task BetDraw(ShmartNumber amount, InputColorGuess col, InputValueGuess? val = null)
|
||||||
|
=> BetDrawInternal(amount, val, col);
|
||||||
|
|
||||||
|
public async Task BetDrawInternal(long amount, InputValueGuess? val, InputColorGuess? col)
|
||||||
|
{
|
||||||
|
var res = await _service.BetDrawAsync(ctx.User.Id,
|
||||||
|
amount,
|
||||||
|
(byte?)val,
|
||||||
|
(byte?)col);
|
||||||
|
|
||||||
|
if (!res.TryPickT0(out var result, out _))
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var eb = _eb.Create(ctx)
|
||||||
|
.WithOkColor()
|
||||||
|
.WithAuthor(ctx.User)
|
||||||
|
.AddField(GetText(strs.guess), GetGuessInfo(val, col), true)
|
||||||
|
.AddField(GetText(strs.card), GetCardInfo(result.Card), true)
|
||||||
|
.AddField(GetText(strs.won), N((long)result.Won), false)
|
||||||
|
.WithImageUrl("attachment://card.png");
|
||||||
|
|
||||||
|
using var img = await GetCardImageAsync(result.Card);
|
||||||
|
await using var imgStream = await img.ToStreamAsync();
|
||||||
|
await ctx.Channel.SendFileAsync(imgStream, "card.png", embed: eb.Build());
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetGuessInfo(InputValueGuess? valG, InputColorGuess? colG)
|
||||||
|
{
|
||||||
|
var val = valG switch
|
||||||
|
{
|
||||||
|
InputValueGuess.H => "Hi ⬆️",
|
||||||
|
InputValueGuess.L => "Lo ⬇️",
|
||||||
|
_ => "❓"
|
||||||
|
};
|
||||||
|
|
||||||
|
var col = colG switch
|
||||||
|
{
|
||||||
|
InputColorGuess.Red => "R 🔴",
|
||||||
|
InputColorGuess.Black => "B ⚫",
|
||||||
|
_ => "❓"
|
||||||
|
};
|
||||||
|
|
||||||
|
return $"{val} / {col}";
|
||||||
|
}
|
||||||
|
private string GetCardInfo(Deck.Card card)
|
||||||
|
{
|
||||||
|
var val = card.Number switch
|
||||||
|
{
|
||||||
|
< 7 => "Lo ⬇️",
|
||||||
|
> 7 => "Hi ⬆️",
|
||||||
|
_ => "7 💀"
|
||||||
|
};
|
||||||
|
|
||||||
|
var col = card.Number == 7
|
||||||
|
? "7 💀"
|
||||||
|
: card.Suit switch
|
||||||
|
{
|
||||||
|
Deck.CardSuit.Diamonds or Deck.CardSuit.Hearts => "R 🔴",
|
||||||
|
_ => "B ⚫"
|
||||||
|
};
|
||||||
|
|
||||||
|
return $"{val} / {col}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum InputValueGuess
|
||||||
|
{
|
||||||
|
High = 0,
|
||||||
|
H = 0,
|
||||||
|
Hi = 0,
|
||||||
|
Low = 1,
|
||||||
|
L = 1,
|
||||||
|
Lo = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum InputColorGuess
|
||||||
|
{
|
||||||
|
R = 0,
|
||||||
|
Red = 0,
|
||||||
|
B = 1,
|
||||||
|
Bl = 1,
|
||||||
|
Black = 1,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -76,17 +76,23 @@ public partial class Gambling
|
|||||||
foreach (var i in imgs)
|
foreach (var i in imgs)
|
||||||
i.Dispose();
|
i.Dispose();
|
||||||
|
|
||||||
|
var imgName = $"coins.{format.FileExtensions.First()}";
|
||||||
|
|
||||||
var msg = count != 1
|
var msg = count != 1
|
||||||
? Format.Bold(ctx.User.ToString())
|
? Format.Bold(GetText(strs.flip_results(count, headCount, tailCount)))
|
||||||
+ " "
|
: GetText(strs.flipped(headCount > 0
|
||||||
+ GetText(strs.flip_results(count, headCount, tailCount))
|
? Format.Bold(GetText(strs.heads))
|
||||||
: Format.Bold(ctx.User.ToString())
|
: Format.Bold(GetText(strs.tails))));
|
||||||
+ " "
|
|
||||||
+ GetText(strs.flipped(headCount > 0
|
var eb = _eb.Create(ctx)
|
||||||
? Format.Bold(GetText(strs.heads))
|
.WithOkColor()
|
||||||
: Format.Bold(GetText(strs.tails))));
|
.WithAuthor(ctx.User)
|
||||||
|
.WithDescription(msg)
|
||||||
|
.WithImageUrl($"attachment://{imgName}");
|
||||||
|
|
||||||
await ctx.Channel.SendFileAsync(stream, $"{count} coins.{format.FileExtensions.First()}", msg);
|
await ctx.Channel.SendFileAsync(stream,
|
||||||
|
imgName,
|
||||||
|
embed: eb.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
|
@@ -345,7 +345,8 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
("award", var name, ulong userId) => GetText(strs.curtr_award(name, userId)),
|
("award", var name, ulong userId) => GetText(strs.curtr_award(name, userId)),
|
||||||
("take", var name, ulong userId) => GetText(strs.curtr_take(name, userId)),
|
("take", var name, ulong userId) => GetText(strs.curtr_take(name, userId)),
|
||||||
("blackjack", _, _) => $"Blackjack - {subType}",
|
("blackjack", _, _) => $"Blackjack - {subType}",
|
||||||
("wheel", _, _) => $"Wheel Of Fortune - {subType}",
|
("wheel", _, _) => $"Lucky Ladder - {subType}",
|
||||||
|
("lula", _, _) => $"Lucky Ladder - {subType}",
|
||||||
("rps", _, _) => $"Rock Paper Scissors - {subType}",
|
("rps", _, _) => $"Rock Paper Scissors - {subType}",
|
||||||
(null, _, _) => null,
|
(null, _, _) => null,
|
||||||
(_, null, _) => null,
|
(_, null, _) => null,
|
||||||
@@ -671,7 +672,7 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
|
|
||||||
var eb = _eb.Create(ctx)
|
var eb = _eb.Create(ctx)
|
||||||
.WithAuthor(ctx.User)
|
.WithAuthor(ctx.User)
|
||||||
.WithFooter(str)
|
.WithDescription(Format.Bold(str))
|
||||||
.AddField(GetText(strs.roll2), result.Roll.ToString(CultureInfo.InvariantCulture))
|
.AddField(GetText(strs.roll2), result.Roll.ToString(CultureInfo.InvariantCulture))
|
||||||
.WithOkColor();
|
.WithOkColor();
|
||||||
|
|
||||||
@@ -772,7 +773,6 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
Scissors = 2
|
Scissors = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo check if trivia is being disposed
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
public async partial Task Rps(InputRpsPick pick, ShmartNumber amount = default)
|
public async partial Task Rps(InputRpsPick pick, ShmartNumber amount = default)
|
||||||
{
|
{
|
||||||
@@ -839,7 +839,7 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
if (!await CheckBetMandatory(amount))
|
if (!await CheckBetMandatory(amount))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var res = await _gs.WofAsync(ctx.User.Id, amount);
|
var res = await _gs.LulaAsync(ctx.User.Id, amount);
|
||||||
if (!res.TryPickT0(out var result, out _))
|
if (!res.TryPickT0(out var result, out _))
|
||||||
{
|
{
|
||||||
await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
|
await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
|
||||||
@@ -871,4 +871,99 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
|
|
||||||
await ctx.Channel.EmbedAsync(eb);
|
await ctx.Channel.EmbedAsync(eb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public enum GambleTestTarget
|
||||||
|
{
|
||||||
|
Slot,
|
||||||
|
BetDraw,
|
||||||
|
BetDrawHL,
|
||||||
|
BetDrawRB,
|
||||||
|
Betflip,
|
||||||
|
BetflipT,
|
||||||
|
Lula,
|
||||||
|
Rps,
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[OwnerOnly]
|
||||||
|
public async partial Task BetTest()
|
||||||
|
{
|
||||||
|
await SendConfirmAsync(GetText(strs.available_tests),
|
||||||
|
Enum.GetValues<GambleTestTarget>()
|
||||||
|
.Select(x => $"`{x}`")
|
||||||
|
.Join(", "));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[OwnerOnly]
|
||||||
|
public async partial Task BetTest(GambleTestTarget target, int tests = 1000)
|
||||||
|
{
|
||||||
|
if (tests <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
await ctx.Channel.TriggerTypingAsync();
|
||||||
|
|
||||||
|
var streak = 0;
|
||||||
|
var maxW = 0;
|
||||||
|
var maxL = 0;
|
||||||
|
|
||||||
|
var dict = new Dictionary<decimal, int>();
|
||||||
|
for (var i = 0; i < tests; i++)
|
||||||
|
{
|
||||||
|
var multi = target switch
|
||||||
|
{
|
||||||
|
GambleTestTarget.BetDraw => (await _gs.BetDrawAsync(ctx.User.Id, 0, 1, 0)).AsT0.Multiplier,
|
||||||
|
GambleTestTarget.BetDrawRB => (await _gs.BetDrawAsync(ctx.User.Id, 0, null, 1)).AsT0.Multiplier,
|
||||||
|
GambleTestTarget.BetDrawHL => (await _gs.BetDrawAsync(ctx.User.Id, 0, 0, null)).AsT0.Multiplier,
|
||||||
|
GambleTestTarget.Slot => (await _gs.SlotAsync(ctx.User.Id, 0)).AsT0.Multiplier,
|
||||||
|
GambleTestTarget.Betflip => (await _gs.BetFlipAsync(ctx.User.Id, 0, 0)).AsT0.Multiplier,
|
||||||
|
GambleTestTarget.BetflipT => (await _gs.BetFlipAsync(ctx.User.Id, 0, 1)).AsT0.Multiplier,
|
||||||
|
GambleTestTarget.Lula => (await _gs.LulaAsync(ctx.User.Id, 0)).AsT0.Multiplier,
|
||||||
|
GambleTestTarget.Rps => (await _gs.RpsAsync(ctx.User.Id, 0, (byte)(i % 3))).AsT0.Multiplier,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(target))
|
||||||
|
};
|
||||||
|
|
||||||
|
if (dict.ContainsKey(multi))
|
||||||
|
dict[multi] += 1;
|
||||||
|
else
|
||||||
|
dict.Add(multi, 1);
|
||||||
|
|
||||||
|
if (multi < 1)
|
||||||
|
{
|
||||||
|
if (streak <= 0)
|
||||||
|
--streak;
|
||||||
|
else
|
||||||
|
streak = -1;
|
||||||
|
|
||||||
|
maxL = Math.Max(maxL, -streak);
|
||||||
|
}
|
||||||
|
else if (multi > 1)
|
||||||
|
{
|
||||||
|
if (streak >= 0)
|
||||||
|
++streak;
|
||||||
|
else
|
||||||
|
streak = 1;
|
||||||
|
|
||||||
|
maxW = Math.Max(maxW, streak);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
decimal payout = 0;
|
||||||
|
foreach (var key in dict.Keys.OrderByDescending(x => x))
|
||||||
|
{
|
||||||
|
sb.AppendLine($"x**{key}** occured `{dict[key]}` times. {dict[key] * 1.0f / tests * 100}%");
|
||||||
|
payout += key * dict[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine($"Longest win streak: `{maxW}`");
|
||||||
|
sb.AppendLine($"Longest lose streak: `{maxL}`");
|
||||||
|
|
||||||
|
await SendConfirmAsync(GetText(strs.test_results_for(target)),
|
||||||
|
sb.ToString(),
|
||||||
|
footer: $"Total Bet: {tests} | Payout: {payout:F0} | {payout * 1.0M / tests * 100}%");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
#nullable disable
|
#nullable disable warnings
|
||||||
using NadekoBot.Db.Models;
|
using NadekoBot.Db.Models;
|
||||||
using NadekoBot.Modules.Gambling.Common;
|
using NadekoBot.Modules.Gambling.Common;
|
||||||
using NadekoBot.Modules.Gambling.Services;
|
using NadekoBot.Modules.Gambling.Services;
|
||||||
@@ -73,178 +73,58 @@ public partial class Gambling
|
|||||||
|
|
||||||
await ctx.Channel.EmbedAsync(embed);
|
await ctx.Channel.EmbedAsync(embed);
|
||||||
}
|
}
|
||||||
|
public sealed class SlotInteraction : NInteraction
|
||||||
[Cmd]
|
|
||||||
[OwnerOnly]
|
|
||||||
public async partial Task SlotTest(int tests = 1000)
|
|
||||||
{
|
{
|
||||||
if (tests <= 0)
|
public SlotInteraction(DiscordSocketClient client, ulong userId, Func<SocketMessageComponent, Task> action) : base(client, userId, action)
|
||||||
return;
|
|
||||||
//multi vs how many times it occured
|
|
||||||
|
|
||||||
int streak = 0;
|
|
||||||
int maxW = 0;
|
|
||||||
int maxL = 0;
|
|
||||||
|
|
||||||
var dict = new Dictionary<decimal, int>();
|
|
||||||
for (var i = 0; i < tests; i++)
|
|
||||||
{
|
{
|
||||||
var res = await _service.SlotAsync(ctx.User.Id, 0);
|
|
||||||
var multi = res.AsT0.Multiplier;
|
|
||||||
if (dict.ContainsKey(multi))
|
|
||||||
dict[multi] += 1;
|
|
||||||
else
|
|
||||||
dict.Add(multi, 1);
|
|
||||||
|
|
||||||
if (multi == 0)
|
|
||||||
{
|
|
||||||
if (streak <= 0)
|
|
||||||
--streak;
|
|
||||||
else
|
|
||||||
streak = -1;
|
|
||||||
|
|
||||||
maxL = Math.Max(maxL, -streak);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (streak >= 0)
|
|
||||||
++streak;
|
|
||||||
else
|
|
||||||
streak = 1;
|
|
||||||
|
|
||||||
maxW = Math.Max(maxW, streak);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
decimal payout = 0;
|
|
||||||
foreach (var key in dict.Keys.OrderByDescending(x => x))
|
|
||||||
{
|
|
||||||
sb.AppendLine($"x{key} occured `{dict[key]}` times. {dict[key] * 1.0f / tests * 100}%");
|
|
||||||
payout += key * dict[key];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.AppendLine();
|
protected override NadekoInteractionData Data { get; } = new(Emoji.Parse("🔁"),
|
||||||
sb.AppendLine($"Longest win streak: `{maxW}`");
|
"slot:again",
|
||||||
sb.AppendLine($"Longest lose streak: `{maxL}`");
|
"Pull Again");
|
||||||
|
|
||||||
await SendConfirmAsync("Slot Test Results",
|
|
||||||
sb.ToString(),
|
|
||||||
footer: $"Total Bet: {tests} | Payout: {payout:F0} | {payout * 1.0M / tests * 100}%");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
public async partial Task Slot(ShmartNumber amount)
|
public async partial Task Slot(ShmartNumber amount)
|
||||||
{
|
{
|
||||||
|
if (!await CheckBetMandatory(amount))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// var slotInteraction = CreateSlotInteractionIntenal(amount);
|
||||||
|
|
||||||
|
await ctx.Channel.TriggerTypingAsync();
|
||||||
|
|
||||||
if (!_runningUsers.Add(ctx.User.Id))
|
if (!_runningUsers.Add(ctx.User.Id))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!await CheckBetMandatory(amount))
|
if (await InternalSlotAsync(amount) is not SlotResult result)
|
||||||
return;
|
|
||||||
|
|
||||||
await ctx.Channel.TriggerTypingAsync();
|
|
||||||
|
|
||||||
var maybeResult = await _service.SlotAsync(ctx.User.Id, amount);
|
|
||||||
|
|
||||||
if (!maybeResult.TryPickT0(out var result, out var error))
|
|
||||||
{
|
{
|
||||||
if (error == GamblingError.InsufficientFunds)
|
await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
|
||||||
await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
lock (_slotStatsLock)
|
var msg = GetSlotMessageInternal(result);
|
||||||
{
|
|
||||||
totalBet += amount;
|
|
||||||
totalPaidOut += result.Won;
|
|
||||||
}
|
|
||||||
|
|
||||||
long ownedAmount;
|
using var image = await GenerateSlotImageAsync(amount, result);
|
||||||
await using (var uow = _db.GetDbContext())
|
await using var imgStream = await image.ToStreamAsync();
|
||||||
{
|
|
||||||
ownedAmount = uow.Set<DiscordUser>().FirstOrDefault(x => x.UserId == ctx.User.Id)?.CurrencyAmount
|
|
||||||
?? 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
var slotBg = await _images.GetSlotBgAsync();
|
|
||||||
using (var bgImage = Image.Load<Rgba32>(slotBg, out _))
|
|
||||||
{
|
|
||||||
var numbers = new int[3];
|
|
||||||
result.Rolls.CopyTo(numbers, 0);
|
|
||||||
|
|
||||||
Color fontColor = Config.Slots.CurrencyFontColor;
|
var eb = _eb.Create(ctx)
|
||||||
|
.WithAuthor(ctx.User)
|
||||||
|
.WithDescription(Format.Bold(msg))
|
||||||
|
.WithImageUrl($"attachment://result.png")
|
||||||
|
.WithOkColor();
|
||||||
|
|
||||||
|
// var inter = slotInteraction.GetInteraction();
|
||||||
|
await ctx.Channel.SendFileAsync(imgStream,
|
||||||
|
"result.png",
|
||||||
|
embed: eb.Build()
|
||||||
|
// components: inter.CreateComponent()
|
||||||
|
);
|
||||||
|
|
||||||
bgImage.Mutate(x => x.DrawText(new()
|
// await inter.RunAsync(resMsg);
|
||||||
{
|
|
||||||
TextOptions = new()
|
|
||||||
{
|
|
||||||
HorizontalAlignment = HorizontalAlignment.Center,
|
|
||||||
VerticalAlignment = VerticalAlignment.Center,
|
|
||||||
WrapTextWidth = 140
|
|
||||||
}
|
|
||||||
},
|
|
||||||
((long)result.Won).ToString(),
|
|
||||||
_fonts.DottyFont.CreateFont(65),
|
|
||||||
fontColor,
|
|
||||||
new(227, 92)));
|
|
||||||
|
|
||||||
var bottomFont = _fonts.DottyFont.CreateFont(50);
|
|
||||||
|
|
||||||
bgImage.Mutate(x => x.DrawText(new()
|
|
||||||
{
|
|
||||||
TextOptions = new()
|
|
||||||
{
|
|
||||||
HorizontalAlignment = HorizontalAlignment.Center,
|
|
||||||
VerticalAlignment = VerticalAlignment.Center,
|
|
||||||
WrapTextWidth = 135
|
|
||||||
}
|
|
||||||
},
|
|
||||||
amount.ToString(),
|
|
||||||
bottomFont,
|
|
||||||
fontColor,
|
|
||||||
new(129, 472)));
|
|
||||||
|
|
||||||
bgImage.Mutate(x => x.DrawText(new()
|
|
||||||
{
|
|
||||||
TextOptions = new()
|
|
||||||
{
|
|
||||||
HorizontalAlignment = HorizontalAlignment.Center,
|
|
||||||
VerticalAlignment = VerticalAlignment.Center,
|
|
||||||
WrapTextWidth = 135
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ownedAmount.ToString(),
|
|
||||||
bottomFont,
|
|
||||||
fontColor,
|
|
||||||
new(325, 472)));
|
|
||||||
//sw.PrintLap("drew red text");
|
|
||||||
|
|
||||||
for (var i = 0; i < 3; i++)
|
|
||||||
{
|
|
||||||
using var img = Image.Load(await _images.GetSlotEmojiAsync(numbers[i]));
|
|
||||||
bgImage.Mutate(x => x.DrawImage(img, new Point(148 + (105 * i), 217), 1f));
|
|
||||||
}
|
|
||||||
|
|
||||||
var multi = result.Multiplier.ToString("0.##");
|
|
||||||
var msg = result.WinType switch
|
|
||||||
{
|
|
||||||
SlotWinType.SingleJoker => GetText(strs.slot_single(CurrencySign, multi)),
|
|
||||||
SlotWinType.DoubleJoker => GetText(strs.slot_two(CurrencySign, multi)),
|
|
||||||
SlotWinType.TrippleNormal => GetText(strs.slot_three(multi)),
|
|
||||||
SlotWinType.TrippleJoker => GetText(strs.slot_jackpot(multi)),
|
|
||||||
_ => GetText(strs.better_luck),
|
|
||||||
};
|
|
||||||
|
|
||||||
await using (var imgStream = await bgImage.ToStreamAsync())
|
|
||||||
{
|
|
||||||
await ctx.Channel.SendFileAsync(imgStream,
|
|
||||||
"result.png",
|
|
||||||
Format.Bold(ctx.User.ToString()) + " " + msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -252,5 +132,156 @@ public partial class Gambling
|
|||||||
_runningUsers.TryRemove(ctx.User.Id);
|
_runningUsers.TryRemove(ctx.User.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// private SlotInteraction CreateSlotInteractionIntenal(long amount)
|
||||||
|
// {
|
||||||
|
// return new SlotInteraction((DiscordSocketClient)ctx.Client,
|
||||||
|
// ctx.User.Id,
|
||||||
|
// async (smc) =>
|
||||||
|
// {
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// if (await InternalSlotAsync(amount) is not SlotResult result)
|
||||||
|
// {
|
||||||
|
// await smc.RespondErrorAsync(_eb, GetText(strs.not_enough(CurrencySign)), true);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// var msg = GetSlotMessageInternal(result);
|
||||||
|
//
|
||||||
|
// using var image = await GenerateSlotImageAsync(amount, result);
|
||||||
|
// await using var imgStream = await image.ToStreamAsync();
|
||||||
|
//
|
||||||
|
// var guid = Guid.NewGuid();
|
||||||
|
// var imgName = $"result_{guid}.png";
|
||||||
|
//
|
||||||
|
// var slotInteraction = CreateSlotInteractionIntenal(amount).GetInteraction();
|
||||||
|
//
|
||||||
|
// await smc.Message.ModifyAsync(m =>
|
||||||
|
// {
|
||||||
|
// m.Content = msg;
|
||||||
|
// m.Attachments = new[]
|
||||||
|
// {
|
||||||
|
// new FileAttachment(imgStream, imgName)
|
||||||
|
// };
|
||||||
|
// m.Components = slotInteraction.CreateComponent();
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// _ = slotInteraction.RunAsync(smc.Message);
|
||||||
|
// }
|
||||||
|
// catch (Exception ex)
|
||||||
|
// {
|
||||||
|
// Log.Error(ex, "Error pulling slot again");
|
||||||
|
// }
|
||||||
|
// // finally
|
||||||
|
// // {
|
||||||
|
// // await Task.Delay(1000);
|
||||||
|
// // _runningUsers.TryRemove(ctx.User.Id);
|
||||||
|
// // }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
private string GetSlotMessageInternal(SlotResult result)
|
||||||
|
{
|
||||||
|
var multi = result.Multiplier.ToString("0.##");
|
||||||
|
var msg = result.WinType switch
|
||||||
|
{
|
||||||
|
SlotWinType.SingleJoker => GetText(strs.slot_single(CurrencySign, multi)),
|
||||||
|
SlotWinType.DoubleJoker => GetText(strs.slot_two(CurrencySign, multi)),
|
||||||
|
SlotWinType.TrippleNormal => GetText(strs.slot_three(multi)),
|
||||||
|
SlotWinType.TrippleJoker => GetText(strs.slot_jackpot(multi)),
|
||||||
|
_ => GetText(strs.better_luck),
|
||||||
|
};
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<SlotResult?> InternalSlotAsync(long amount)
|
||||||
|
{
|
||||||
|
var maybeResult = await _service.SlotAsync(ctx.User.Id, amount);
|
||||||
|
|
||||||
|
if (!maybeResult.TryPickT0(out var result, out var error))
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_slotStatsLock)
|
||||||
|
{
|
||||||
|
totalBet += amount;
|
||||||
|
totalPaidOut += result.Won;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<Image<Rgba32>> GenerateSlotImageAsync(long amount, SlotResult result)
|
||||||
|
{
|
||||||
|
long ownedAmount;
|
||||||
|
await using (var uow = _db.GetDbContext())
|
||||||
|
{
|
||||||
|
ownedAmount = uow.Set<DiscordUser>().FirstOrDefault(x => x.UserId == ctx.User.Id)?.CurrencyAmount
|
||||||
|
?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var slotBg = await _images.GetSlotBgAsync();
|
||||||
|
var bgImage = Image.Load<Rgba32>(slotBg, out _);
|
||||||
|
var numbers = new int[3];
|
||||||
|
result.Rolls.CopyTo(numbers, 0);
|
||||||
|
|
||||||
|
Color fontColor = Config.Slots.CurrencyFontColor;
|
||||||
|
|
||||||
|
bgImage.Mutate(x => x.DrawText(new()
|
||||||
|
{
|
||||||
|
TextOptions = new()
|
||||||
|
{
|
||||||
|
HorizontalAlignment = HorizontalAlignment.Center,
|
||||||
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
|
WrapTextWidth = 140
|
||||||
|
}
|
||||||
|
},
|
||||||
|
((long)result.Won).ToString(),
|
||||||
|
_fonts.DottyFont.CreateFont(65),
|
||||||
|
fontColor,
|
||||||
|
new(227, 92)));
|
||||||
|
|
||||||
|
var bottomFont = _fonts.DottyFont.CreateFont(50);
|
||||||
|
|
||||||
|
bgImage.Mutate(x => x.DrawText(new()
|
||||||
|
{
|
||||||
|
TextOptions = new()
|
||||||
|
{
|
||||||
|
HorizontalAlignment = HorizontalAlignment.Center,
|
||||||
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
|
WrapTextWidth = 135
|
||||||
|
}
|
||||||
|
},
|
||||||
|
amount.ToString(),
|
||||||
|
bottomFont,
|
||||||
|
fontColor,
|
||||||
|
new(129, 472)));
|
||||||
|
|
||||||
|
bgImage.Mutate(x => x.DrawText(new()
|
||||||
|
{
|
||||||
|
TextOptions = new()
|
||||||
|
{
|
||||||
|
HorizontalAlignment = HorizontalAlignment.Center,
|
||||||
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
|
WrapTextWidth = 135
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ownedAmount.ToString(),
|
||||||
|
bottomFont,
|
||||||
|
fontColor,
|
||||||
|
new(325, 472)));
|
||||||
|
//sw.PrintLap("drew red text");
|
||||||
|
|
||||||
|
for (var i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
using var img = Image.Load(await _images.GetSlotEmojiAsync(numbers[i]));
|
||||||
|
bgImage.Mutate(x => x.DrawImage(img, new Point(148 + (105 * i), 217), 1f));
|
||||||
|
}
|
||||||
|
|
||||||
|
return bgImage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,4 +1,6 @@
|
|||||||
namespace NadekoBot.Modules.Gambling.Common;
|
using Nadeko.Econ;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Gambling.Common;
|
||||||
|
|
||||||
public class QuadDeck : Deck
|
public class QuadDeck : Deck
|
||||||
{
|
{
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
using Nadeko.Econ.Gambling;
|
using Nadeko.Econ.Gambling;
|
||||||
|
using Nadeko.Econ.Gambling.Betdraw;
|
||||||
using Nadeko.Econ.Gambling.Rps;
|
using Nadeko.Econ.Gambling.Rps;
|
||||||
using OneOf;
|
using OneOf;
|
||||||
|
|
||||||
@@ -7,10 +8,11 @@ namespace NadekoBot.Modules.Gambling;
|
|||||||
|
|
||||||
public interface IGamblingService
|
public interface IGamblingService
|
||||||
{
|
{
|
||||||
Task<OneOf<LuLaResult, GamblingError>> WofAsync(ulong userId, long amount);
|
Task<OneOf<LuLaResult, GamblingError>> LulaAsync(ulong userId, long amount);
|
||||||
Task<OneOf<BetrollResult, GamblingError>> BetRollAsync(ulong userId, long amount);
|
Task<OneOf<BetrollResult, GamblingError>> BetRollAsync(ulong userId, long amount);
|
||||||
Task<OneOf<BetflipResult, GamblingError>> BetFlipAsync(ulong userId, long amount, byte guess);
|
Task<OneOf<BetflipResult, GamblingError>> BetFlipAsync(ulong userId, long amount, byte guess);
|
||||||
Task<OneOf<SlotResult, GamblingError>> SlotAsync(ulong userId, long amount);
|
Task<OneOf<SlotResult, GamblingError>> SlotAsync(ulong userId, long amount);
|
||||||
Task<FlipResult[]> FlipAsync(int count);
|
Task<FlipResult[]> FlipAsync(int count);
|
||||||
Task<OneOf<RpsResult, GamblingError>> RpsAsync(ulong userId, long amount, byte pick);
|
Task<OneOf<RpsResult, GamblingError>> RpsAsync(ulong userId, long amount, byte pick);
|
||||||
|
Task<OneOf<BetdrawResult, GamblingError>> BetDrawAsync(ulong userId, long amount, byte? guessValue, byte? guessColor);
|
||||||
}
|
}
|
@@ -1,5 +1,6 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
using Nadeko.Econ.Gambling;
|
using Nadeko.Econ.Gambling;
|
||||||
|
using Nadeko.Econ.Gambling.Betdraw;
|
||||||
using Nadeko.Econ.Gambling.Rps;
|
using Nadeko.Econ.Gambling.Rps;
|
||||||
using NadekoBot.Modules.Gambling.Services;
|
using NadekoBot.Modules.Gambling.Services;
|
||||||
using OneOf;
|
using OneOf;
|
||||||
@@ -17,9 +18,7 @@ public sealed class NewGamblingService : IGamblingService, INService
|
|||||||
_cs = cs;
|
_cs = cs;
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo input checks
|
public async Task<OneOf<LuLaResult, GamblingError>> LulaAsync(ulong userId, long amount)
|
||||||
// todo ladder of fortune
|
|
||||||
public async Task<OneOf<LuLaResult, GamblingError>> WofAsync(ulong userId, long amount)
|
|
||||||
{
|
{
|
||||||
if (amount < 0)
|
if (amount < 0)
|
||||||
throw new ArgumentOutOfRangeException(nameof(amount));
|
throw new ArgumentOutOfRangeException(nameof(amount));
|
||||||
@@ -64,9 +63,9 @@ public sealed class NewGamblingService : IGamblingService, INService
|
|||||||
var game = new BetrollGame(_bcs.Data.BetRoll.Pairs
|
var game = new BetrollGame(_bcs.Data.BetRoll.Pairs
|
||||||
.Select(x => (x.WhenAbove, (decimal)x.MultiplyBy))
|
.Select(x => (x.WhenAbove, (decimal)x.MultiplyBy))
|
||||||
.ToList());
|
.ToList());
|
||||||
|
|
||||||
var result = game.Roll(amount);
|
var result = game.Roll(amount);
|
||||||
|
|
||||||
var won = (long)result.Won;
|
var won = (long)result.Won;
|
||||||
if (won > 0)
|
if (won > 0)
|
||||||
{
|
{
|
||||||
@@ -81,6 +80,9 @@ public sealed class NewGamblingService : IGamblingService, INService
|
|||||||
if (amount < 0)
|
if (amount < 0)
|
||||||
throw new ArgumentOutOfRangeException(nameof(amount));
|
throw new ArgumentOutOfRangeException(nameof(amount));
|
||||||
|
|
||||||
|
if (guess > 1)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(guess));
|
||||||
|
|
||||||
if (amount > 0)
|
if (amount > 0)
|
||||||
{
|
{
|
||||||
var isTakeSuccess = await _cs.RemoveAsync(userId, amount, new("betflip", "bet"));
|
var isTakeSuccess = await _cs.RemoveAsync(userId, amount, new("betflip", "bet"));
|
||||||
@@ -102,6 +104,42 @@ public sealed class NewGamblingService : IGamblingService, INService
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<OneOf<BetdrawResult, GamblingError>> BetDrawAsync(ulong userId, long amount, byte? guessValue, byte? guessColor)
|
||||||
|
{
|
||||||
|
if (amount < 0)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(amount));
|
||||||
|
|
||||||
|
if (guessColor is null && guessValue is null)
|
||||||
|
throw new ArgumentNullException();
|
||||||
|
|
||||||
|
if (guessColor > 1)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(guessColor));
|
||||||
|
|
||||||
|
if (guessValue > 1)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(guessValue));
|
||||||
|
|
||||||
|
if (amount > 0)
|
||||||
|
{
|
||||||
|
var isTakeSuccess = await _cs.RemoveAsync(userId, amount, new("betflip", "bet"));
|
||||||
|
|
||||||
|
if (!isTakeSuccess)
|
||||||
|
{
|
||||||
|
return GamblingError.InsufficientFunds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var game = new BetdrawGame();
|
||||||
|
var result = game.Draw((BetdrawValueGuess?)guessValue, (BetdrawColorGuess?)guessColor, amount);
|
||||||
|
|
||||||
|
var won = (long)result.Won;
|
||||||
|
if (won > 0)
|
||||||
|
{
|
||||||
|
await _cs.AddAsync(userId, won, new("betflip", "win"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<OneOf<SlotResult, GamblingError>> SlotAsync(ulong userId, long amount)
|
public async Task<OneOf<SlotResult, GamblingError>> SlotAsync(ulong userId, long amount)
|
||||||
{
|
{
|
||||||
@@ -132,6 +170,9 @@ public sealed class NewGamblingService : IGamblingService, INService
|
|||||||
|
|
||||||
public Task<FlipResult[]> FlipAsync(int count)
|
public Task<FlipResult[]> FlipAsync(int count)
|
||||||
{
|
{
|
||||||
|
if (count < 1)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(count));
|
||||||
|
|
||||||
var game = new BetflipGame(0);
|
var game = new BetflipGame(0);
|
||||||
|
|
||||||
var results = new FlipResult[count];
|
var results = new FlipResult[count];
|
||||||
|
@@ -108,7 +108,7 @@ public partial class NSFW : NadekoModule<ISearchImagesService>
|
|||||||
return t;
|
return t;
|
||||||
});
|
});
|
||||||
|
|
||||||
await ReplyConfirmLocalizedAsync(strs.autohentai_started(interval, string.Join(", ", tags)));
|
await SendConfirmAsync($"Autohentai started. Interval: {interval}, Tags: {string.Join(", ", tags)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -653,7 +653,7 @@ rolluo:
|
|||||||
- "7"
|
- "7"
|
||||||
- "3d5"
|
- "3d5"
|
||||||
nroll:
|
nroll:
|
||||||
desc: "Rolls in a given range. If you specify just one number instead of the range, it will role from 0 to that number."
|
desc: "Rolls in a given range. If you specify just one number instead of the range, it will roll from 0 to that number."
|
||||||
args:
|
args:
|
||||||
- "5"
|
- "5"
|
||||||
- "5-15"
|
- "5-15"
|
||||||
@@ -2231,4 +2231,23 @@ eval:
|
|||||||
args:
|
args:
|
||||||
- "123 / 4.5f"
|
- "123 / 4.5f"
|
||||||
- "await ctx.OkAsync();"
|
- "await ctx.OkAsync();"
|
||||||
- 'await ctx.SendConfirmAsync("uwu");'
|
- 'await ctx.SendConfirmAsync("uwu");'
|
||||||
|
betdraw:
|
||||||
|
desc: |-
|
||||||
|
Bet on the card value and/or color. Specify the amount followed by your guess.
|
||||||
|
You can specify `r` or `b` for red or black, and `h` or `l` for high or low.
|
||||||
|
You can specify only h/l or only r/b or both.
|
||||||
|
Returns are high but **7 always loses**.
|
||||||
|
args:
|
||||||
|
- "50 r"
|
||||||
|
- "200 b l"
|
||||||
|
- "1000 h"
|
||||||
|
- "38 hi black"
|
||||||
|
bettest:
|
||||||
|
desc: |-
|
||||||
|
Tests a betting command by specifying the name followed by the number of tests. Some have multiple variations.
|
||||||
|
See the list of all tests by specifying no parameters.
|
||||||
|
args:
|
||||||
|
- ""
|
||||||
|
- "betflip 1000"
|
||||||
|
- "slot 2000"
|
||||||
|
@@ -81,7 +81,6 @@
|
|||||||
"boostdel_on": "Boost messages will be deleted after {0} seconds.",
|
"boostdel_on": "Boost messages will be deleted after {0} seconds.",
|
||||||
"hierarchy": "You can't use this command on users with a role higher or equal than yours (or mine) in the role hierarchy.",
|
"hierarchy": "You can't use this command on users with a role higher or equal than yours (or mine) in the role hierarchy.",
|
||||||
"role_too_high": "You can't use this command with roles which are above your highest role, unless you're server administrator.",
|
"role_too_high": "You can't use this command with roles which are above your highest role, unless you're server administrator.",
|
||||||
"images_loading": "Images will be reloaded within a few seconds.",
|
|
||||||
"insuf_perms_i": "I have insufficient permissions.",
|
"insuf_perms_i": "I have insufficient permissions.",
|
||||||
"insuf_perms_u": "You have insufficient permissions.",
|
"insuf_perms_u": "You have insufficient permissions.",
|
||||||
"invalid_format": "Invalid input format.",
|
"invalid_format": "Invalid input format.",
|
||||||
@@ -216,11 +215,13 @@
|
|||||||
"better_luck": "Better luck next time ^_^",
|
"better_luck": "Better luck next time ^_^",
|
||||||
"br_win": "Congratulations! You won {0} for rolling above {1}",
|
"br_win": "Congratulations! You won {0} for rolling above {1}",
|
||||||
"deck_reshuffled": "Deck reshuffled.",
|
"deck_reshuffled": "Deck reshuffled.",
|
||||||
"flipped": "flipped {0}.",
|
"flipped": "Flipped {0}",
|
||||||
"flip_guess": "You guessed it! You won {0}",
|
"flip_guess": "You guessed it! You won {0}",
|
||||||
"flip_invalid": "Invalid number specified. You can flip 1 to {0} coins.",
|
"flip_invalid": "Invalid number specified. You can flip 1 to {0} coins.",
|
||||||
"flip_results": "Flipped {0} coins. {1} heads, {2} tails.",
|
"flip_results": "Flipped {0} coins. {1} heads, {2} tails.",
|
||||||
"cards_left": "{0} cards left in the deck.",
|
"cards_left": "{0} cards left in the deck.",
|
||||||
|
"cards": "Cards",
|
||||||
|
"hand_value": "Hand value",
|
||||||
"gifted": "has gifted {0} to {1}",
|
"gifted": "has gifted {0} to {1}",
|
||||||
"has": "{0} has {1}",
|
"has": "{0} has {1}",
|
||||||
"heads": "Head",
|
"heads": "Head",
|
||||||
@@ -232,11 +233,13 @@
|
|||||||
"no_more_cards": "No more cards in the deck.",
|
"no_more_cards": "No more cards in the deck.",
|
||||||
"raffled_user": "Raffled user",
|
"raffled_user": "Raffled user",
|
||||||
"roll2": "Roll",
|
"roll2": "Roll",
|
||||||
"slot_bet": "Bet",
|
"rolls": "Rolls",
|
||||||
"slot_jackpot": "WOAAHHHHHH!!! Congratulations!!! x{0}",
|
"slot_jackpot": "WOAAHHHHHH!!! Congratulations!!! x{0}",
|
||||||
"slot_single": "A single {0}, x{1}",
|
"slot_single": "A single {0}, x{1}",
|
||||||
"slot_three": "Wow! Lucky! Three of a kind! x{0}",
|
"slot_three": "Wow! Lucky! Three of a kind! x{0}",
|
||||||
"slot_two": "Good job! Two {0} - bet x{1}",
|
"slot_two": "Good job! Two {0} - bet x{1}",
|
||||||
|
"available_tests": "Available Tests",
|
||||||
|
"test_results_for": "Test results for {0}",
|
||||||
"won": "Won",
|
"won": "Won",
|
||||||
"multiplier": "Multiplier",
|
"multiplier": "Multiplier",
|
||||||
"tails": "Tail",
|
"tails": "Tail",
|
||||||
@@ -256,7 +259,6 @@
|
|||||||
"usage": "Usage",
|
"usage": "Usage",
|
||||||
"options": "Options",
|
"options": "Options",
|
||||||
"requires": "Requires",
|
"requires": "Requires",
|
||||||
"autohentai_started": "Autohentai started. Reposting every {0}s with one of the following tags:\n{1}",
|
|
||||||
"tag": "Tag",
|
"tag": "Tag",
|
||||||
"animal_race": "Animal race",
|
"animal_race": "Animal race",
|
||||||
"animal_race_failed": "Failed to start since there was not enough participants.",
|
"animal_race_failed": "Failed to start since there was not enough participants.",
|
||||||
@@ -319,7 +321,6 @@
|
|||||||
"rps_win": "{0} won! {1} beats {2}",
|
"rps_win": "{0} won! {1} beats {2}",
|
||||||
"submissions_closed": "Submissions closed",
|
"submissions_closed": "Submissions closed",
|
||||||
"animal_race_already_started": "Animal Race is already running.",
|
"animal_race_already_started": "Animal Race is already running.",
|
||||||
"total_average": "Total: {0} Average: {1}",
|
|
||||||
"category": "Category",
|
"category": "Category",
|
||||||
"cleverbot_disabled": "Disabled cleverbot on this server.",
|
"cleverbot_disabled": "Disabled cleverbot on this server.",
|
||||||
"cleverbot_enabled": "Enabled cleverbot on this server.",
|
"cleverbot_enabled": "Enabled cleverbot on this server.",
|
||||||
@@ -327,7 +328,6 @@
|
|||||||
"curgen_enabled": "Currency generation has been enabled on this channel.",
|
"curgen_enabled": "Currency generation has been enabled on this channel.",
|
||||||
"curgen_pl": "{0} random {1} appeared!",
|
"curgen_pl": "{0} random {1} appeared!",
|
||||||
"curgen_sn": "A random {0} appeared!",
|
"curgen_sn": "A random {0} appeared!",
|
||||||
"failed_loading_question": "Failed loading a question.",
|
|
||||||
"game_started": "Game started",
|
"game_started": "Game started",
|
||||||
"hangman_game_started": "Hangman game started",
|
"hangman_game_started": "Hangman game started",
|
||||||
"hangman_running": "Hangman game already running on this channel.",
|
"hangman_running": "Hangman game already running on this channel.",
|
||||||
@@ -357,10 +357,7 @@
|
|||||||
"vs": "{0} vs {1}",
|
"vs": "{0} vs {1}",
|
||||||
"attempting_to_queue": "Attempting to queue {0} tracks...",
|
"attempting_to_queue": "Attempting to queue {0} tracks...",
|
||||||
"dir_queue_complete": "Directory queue complete.",
|
"dir_queue_complete": "Directory queue complete.",
|
||||||
"fairplay": "Fairplay",
|
|
||||||
"finished_track": "Track Finished",
|
"finished_track": "Track Finished",
|
||||||
"fp_disabled": "Fair play disabled.",
|
|
||||||
"fp_enabled": "Fair play enabled.",
|
|
||||||
"from_position": "From position",
|
"from_position": "From position",
|
||||||
"id": "Id",
|
"id": "Id",
|
||||||
"now_playing": "Now playing",
|
"now_playing": "Now playing",
|
||||||
@@ -447,10 +444,8 @@
|
|||||||
"word_filter_channel_on": "Word filtering enabled on this channel.",
|
"word_filter_channel_on": "Word filtering enabled on this channel.",
|
||||||
"word_filter_server_off": "Word filtering disabled on this server.",
|
"word_filter_server_off": "Word filtering disabled on this server.",
|
||||||
"word_filter_server_on": "Word filtering enabled on this server.",
|
"word_filter_server_on": "Word filtering enabled on this server.",
|
||||||
"avatar_none": "User {0} has no avatar set.",
|
|
||||||
"abilities": "Abilities",
|
"abilities": "Abilities",
|
||||||
"anime_no_fav": "No favorite anime yet",
|
"anime_no_fav": "No favorite anime yet",
|
||||||
"atl_ad_started": "Started automatic translation of messages on this channel. User messages will be auto-deleted.",
|
|
||||||
"atl_removed": "your auto-translate language has been removed.",
|
"atl_removed": "your auto-translate language has been removed.",
|
||||||
"atl_set": "Your auto-translate language has been set to {0}>{1}",
|
"atl_set": "Your auto-translate language has been set to {0}>{1}",
|
||||||
"atl_started": "Started automatic translation of messages on this channel.",
|
"atl_started": "Started automatic translation of messages on this channel.",
|
||||||
@@ -458,6 +453,8 @@
|
|||||||
"atl_not_enabled": "Automatic translation is not enabled on this channel or you've provided an invalid language.",
|
"atl_not_enabled": "Automatic translation is not enabled on this channel or you've provided an invalid language.",
|
||||||
"bad_input_format": "Bad input format, or something went wrong.",
|
"bad_input_format": "Bad input format, or something went wrong.",
|
||||||
"card_not_found": "Couldn't find that card.",
|
"card_not_found": "Couldn't find that card.",
|
||||||
|
"card": "Card",
|
||||||
|
"guess": "Guess",
|
||||||
"catfact": "fact",
|
"catfact": "fact",
|
||||||
"chapters": "Chapters",
|
"chapters": "Chapters",
|
||||||
"comic_number": "Comic #",
|
"comic_number": "Comic #",
|
||||||
@@ -483,7 +480,6 @@
|
|||||||
"height_weight": "Height/Weight",
|
"height_weight": "Height/Weight",
|
||||||
"height_weight_val": "{0}m/{1}kg",
|
"height_weight_val": "{0}m/{1}kg",
|
||||||
"humidity": "Humidity",
|
"humidity": "Humidity",
|
||||||
"image_search_for": "Image search for:",
|
|
||||||
"imdb_fail": "Failed to find that movie.",
|
"imdb_fail": "Failed to find that movie.",
|
||||||
"invalid_lang": "Invalid source or target language.",
|
"invalid_lang": "Invalid source or target language.",
|
||||||
"jokes_not_loaded": "Jokes not loaded.",
|
"jokes_not_loaded": "Jokes not loaded.",
|
||||||
@@ -506,7 +502,6 @@
|
|||||||
"pokemon_none": "No pokemon found.",
|
"pokemon_none": "No pokemon found.",
|
||||||
"rating": "Rating",
|
"rating": "Rating",
|
||||||
"score": "Score:",
|
"score": "Score:",
|
||||||
"search_for": "Search for:",
|
|
||||||
"short_url": "Short url",
|
"short_url": "Short url",
|
||||||
"something_went_wrong": "Something went wrong.",
|
"something_went_wrong": "Something went wrong.",
|
||||||
"specify_search_params": "Please specify search parameters.",
|
"specify_search_params": "Please specify search parameters.",
|
||||||
@@ -793,7 +788,6 @@
|
|||||||
"config_prop_not_found": "Property {0} not found on {1} configuration",
|
"config_prop_not_found": "Property {0} not found on {1} configuration",
|
||||||
"config_list": "Config list",
|
"config_list": "Config list",
|
||||||
"bot_strings_reloaded": "Bot strings have been reloaded.",
|
"bot_strings_reloaded": "Bot strings have been reloaded.",
|
||||||
"level_req": "Level Req.",
|
|
||||||
"xpn_setting_global": "Global Level-Up notifications",
|
"xpn_setting_global": "Global Level-Up notifications",
|
||||||
"xpn_setting_server": "Server Level-Up notifications",
|
"xpn_setting_server": "Server Level-Up notifications",
|
||||||
"xpn_notif_channel": "In the channel where you sent the last message.",
|
"xpn_notif_channel": "In the channel where you sent the last message.",
|
||||||
@@ -902,7 +896,6 @@
|
|||||||
"mass_ban_in_progress": "Banning {0} users...",
|
"mass_ban_in_progress": "Banning {0} users...",
|
||||||
"mass_ban_completed": "Banned {0} users.",
|
"mass_ban_completed": "Banned {0} users.",
|
||||||
"mass_kill_completed": "Mass Banning and Blacklisting of {0} users is complete.",
|
"mass_kill_completed": "Mass Banning and Blacklisting of {0} users is complete.",
|
||||||
"failed_finding_novel": "Can't find that novel. Make sure you've typed the exact full name, and that it exists on novelupdates.com",
|
|
||||||
"club_transfered": "Ownership of the club {0} has been transferred to {1}",
|
"club_transfered": "Ownership of the club {0} has been transferred to {1}",
|
||||||
"club_transfer_failed": "Transfer failed. You must be the club owner. Target must be a member of your club.",
|
"club_transfer_failed": "Transfer failed. You must be the club owner. Target must be a member of your club.",
|
||||||
"roll_duel_challenge": "challenged {1} for a roll duel for {2}",
|
"roll_duel_challenge": "challenged {1} for a roll duel for {2}",
|
||||||
@@ -913,11 +906,6 @@
|
|||||||
"account_not_found": "That account does not exist or is set to private.",
|
"account_not_found": "That account does not exist or is set to private.",
|
||||||
"ninja_not_found": "Currency with that name was not found or an invalid league name was provided.",
|
"ninja_not_found": "Currency with that name was not found or an invalid league name was provided.",
|
||||||
"leagues_not_found": "Unable to retrieve data from Path of Exile API.",
|
"leagues_not_found": "Unable to retrieve data from Path of Exile API.",
|
||||||
"reaction_roles_message": "**Roles:** {0}\n**Content:** {1}",
|
|
||||||
"no_reaction_roles": "There are no ReactionRole features enabled on this server.",
|
|
||||||
"reaction_cant_access": "I can't access {0} reaction. You can only use emotes from servers I'm in.",
|
|
||||||
"reaction_role_removed": "Removed ReactionRole message #{0}",
|
|
||||||
"reaction_roles_full": "You've reached the limit on ReactionRole messages. You have to delete some.",
|
|
||||||
"reminder_list": "List of reminders",
|
"reminder_list": "List of reminders",
|
||||||
"reminder_server_list": "List of server reminders",
|
"reminder_server_list": "List of server reminders",
|
||||||
"reminder_deleted": "Reminder #{0} was deleted.",
|
"reminder_deleted": "Reminder #{0} was deleted.",
|
||||||
@@ -966,7 +954,6 @@
|
|||||||
"module_description_xp": "Gain xp based on chat activity, check users' xp cards",
|
"module_description_xp": "Gain xp based on chat activity, check users' xp cards",
|
||||||
"module_description_medusa": "**Bot Owner only.** Load, unload and handle dynamic modules. Read more [here](https://nadekobot.readthedocs.io/en/latest/medusa/creating-a-medusa/)",
|
"module_description_medusa": "**Bot Owner only.** Load, unload and handle dynamic modules. Read more [here](https://nadekobot.readthedocs.io/en/latest/medusa/creating-a-medusa/)",
|
||||||
"module_description_missing": "Description is missing for this module.",
|
"module_description_missing": "Description is missing for this module.",
|
||||||
"obsolete_use": "⚠ Obsolete, use {0} instead.",
|
|
||||||
"purge_user_confirm": "Are you sure that you want to purge {0} from the database?",
|
"purge_user_confirm": "Are you sure that you want to purge {0} from the database?",
|
||||||
"expr_import_no_input": "Invalid input. No valid file upload or input text found.",
|
"expr_import_no_input": "Invalid input. No valid file upload or input text found.",
|
||||||
"expr_import_invalid_data": "Unable to parse the file. Make sure it's a valid .yml file",
|
"expr_import_invalid_data": "Unable to parse the file. Make sure it's a valid .yml file",
|
||||||
|
Reference in New Issue
Block a user